Documentation / v1

From first request
to production deploy.

Start with a local install, connect payments and external services, deploy on your own infrastructure, and keep the platform updated. Beginner guides include the clicks on each provider's dashboard, not just the environment variables.

27 guides 6 tracks ENsource
Start here8 min read

Platform overview

FastRestful is a production-oriented API product platform. It combines a public marketplace, versioned API releases, a metered gateway, subscriptions and billing, documentation, a consumer console, a vendor studio, and a staff control plane in one Django + frontend system.

New here? Follow the beginner path: Install locally, then make your first gateway request. Connect payments only when you want to charge money, and read Deployment before going public.

What FastRestful is

FastRestful is not a single API. It is the product infrastructure around APIs: the storefront, the contract boundary, the request path, the money, the content, and the operations. A team can run one API or a multi-vendor marketplace from the same installation.

SurfaceWho uses itWhat it owns
Public siteVisitors and buyersMarketplace, API detail pages, docs, pricing, blog, status, vendor profiles, legal pages.
Consumer consoleAPI subscribersApplications, keys, subscriptions, explorer, requests, analytics, invoices, team, support.
Vendor studioAPI publishersProducts, releases, endpoints, plans, consumers, monetization, payouts, portal, support.
Staff control planePlatform operatorsTraffic, vendors, billing, payouts, support, moderation, CMS, announcements, locales, audit.

Single-vendor and multi-vendor modes

The platform starts in single-vendor mode. Only the organization marked as the platform vendor can publish public APIs. A superuser can enable multi-vendor mode in Site settings, after which any organization with an approved vendor profile can publish.

  • Single-vendor: one curated API business, one platform vendor, simpler governance.
  • Multi-vendor: RapidAPI-style marketplace where independent vendors list, price, and support their own APIs.
  • The switch is enforced at the model layer, so API, admin, and vendor studio agree.
  • Vendor onboarding, profile review, and separation of duties remain active in both modes.

Architecture at a glance

request path
Browser / SDK
   │
   ├── public site + consoles ──► Django REST API ──► PostgreSQL
   │                                  │
   │                                  ├── Redis (cache, throttles, Celery)
   │                                  ├── S3 / CloudFront (media, specs)
   │                                  └── provider webhooks (payments, payouts)
   │
   └── API product request ──► metered gateway ──► vendor upstream
                                  │
                                  ├── API key + subscription
                                  ├── quota + rate limit + entitlement
                                  ├── SSRF checks + idempotency + retries
                                  └── usage event + redacted request log

The stack

LayerTechnology
BackendDjango 5.1+, Django REST Framework, SimpleJWT, drf-spectacular, Celery
FrontendReact 19, Next-compatible app router, Vite, TypeScript, Tailwind/shadcn primitives
DatabaseSQLite for local development; PostgreSQL 16 in production
Cache / queueLocMem locally; Redis in production; Celery worker + beat for scheduled work
StorageLocal filesystem by default; S3 + CloudFront recommended in production
PaymentsStripe, PayPal, Flutterwave, Paystack, Razorpay
EmailSMTP, SES, Mailgun, SendGrid, Postmark, Brevo, Mailjet, SparkPost, Resend

Product principles

  • Tenant isolation is server-side. The active workspace is explicit, object permissions are rechecked, and public serializers are separate from private ones.
  • Releases are immutable. A live contract cannot be edited in place; cut a new version and promote it.
  • Provider truth wins. Checkout creation never grants entitlement; signed provider webhooks settle money state.
  • Keys are secrets. Raw API keys are shown once, hashed for lookup, and encrypted for server-side gateway use.
  • Operations leave evidence. Audit events, webhook receipts, request logs, and moderation decisions are append-only where it matters.
  • One design system. Signal Grid tokens, semantic surfaces, monospace metadata, and accessible states across every surface.

Where to go next

Choose the path that matches what you are doing. You do not need to read the whole documentation set in order.

I want to…Read these guides in order
See it running locallyInstallation → Quickstart
Charge customersPayment gateways → Billing & payouts
Connect email, storage, or SSOExternal integrations
Put it on a public serverDeployment → Security → Backups & upgrades
Install official updatesLicensing & official updates
Operate the platform day to dayOperations → Observability → Troubleshooting

Evaluating the product? Read the feature inventory. Looking for a specific endpoint? Use the API reference.

Start here10 min read

Install FastRestful on your computer

A complete first install for someone who has never run a Django or Node application. Follow the steps in order. Every step ends with a check so you know it worked before moving on.

Before you start

You do not need Docker, Redis, cloud storage, or a payment account for the local demo. SQLite and an in-memory cache keep the first run simple.

You needWhyHow to check
Python 3.11Runs the Django backend.python3.11 --version
Node.js 22.13+Runs the frontend and its build tools.node --version
GitDownloads the source code.git --version
Two terminal windowsOne runs the backend, one runs the frontend.Open Terminal twice.
Keep both servers running. The backend and frontend are separate processes. Closing a terminal stops that process. Use Ctrl+C in a terminal to stop a server when you are done.

1. Check your tools

Run these commands in your terminal. If one is missing, install it first and reopen the terminal.

check versions
python3.11 --version
node --version
npm --version
git --version

You should see Python 3.11, Node 22.13 or newer, and an npm version. If python3.11 is not found, install Python 3.11 from python.org. If Node is older than 22.13, install the current LTS release from nodejs.org.

Windows: use py -3.11 wherever this guide says python3.11, and activate the environment with .venv\Scripts\Activate.ps1.

2. Download the code

shell
git clone <your-fastrestful-repository> fastrestful
cd fastrestful
npm ci

npm ci installs the exact frontend dependency versions from the lock file. It can take a few minutes. You should end with a node_modules folder and no error message.

3. Create the backend environment

A virtual environment keeps FastRestful's Python packages separate from the rest of your computer.

shell
cd backend
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

On Windows PowerShell, activate with .venv\Scripts\Activate.ps1 instead of source .venv/bin/activate. When the environment is active, your prompt usually starts with (.venv).

Checkpoint: run python3.11 -m django --version. It should print a Django 5.x version. If it fails, the virtual environment is not active or the install did not finish.

4. Create the backend configuration file

Copy the example file, then generate two secrets and paste them into backend/.env.

shell
cp .env.example .env
python3.11 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
python3.11 -c "import secrets; print(secrets.token_urlsafe(64))"
  1. Open backend/.env in any text editor.
  2. Paste the first output into FIELD_ENCRYPTION_KEY=. This encrypts API keys and provider secrets.
  3. Paste the second output into DJANGO_SECRET_KEY=.
  4. Leave DJANGO_DEBUG=True, DATABASE_URL=sqlite:///db.sqlite3, and FRONTEND_URL=http://localhost:5055 for local development.
Never commit .env. It contains secrets. The repository already ignores it. If you change the frontend port later, update FRONTEND_URL, CORS_ALLOWED_ORIGINS, and CSRF_TRUSTED_ORIGINS to match.

5. Create the database and demo data

Make sure the virtual environment is still active and you are inside backend/.

shell
python3.11 manage.py migrate
python3.11 manage.py seed_demo
python3.11 manage.py seed_apis
python3.11 manage.py seed_docs
python3.11 manage.py seed_engagement

migrate creates the database tables. The seed commands create a demo marketplace, API products, documentation content, and community profiles. This creates a local backend/db.sqlite3 file.

Local-only credentials. seed_demo creates superuser admin / Admin12345!. The engagement seed creates builder profiles with password Demo12345!. Never reuse these credentials in production.

6. Start the backend

terminal 1
# inside backend/, with the virtual environment active
python3.11 manage.py runserver 127.0.0.1:5056

Leave this terminal open. The backend is ready when it prints Starting development server at http://127.0.0.1:5056/. Check it in another terminal with:

check backend
curl http://127.0.0.1:5056/api/health/live

7. Start the frontend

Open the second terminal and go back to the repository root. The backend must stay running in terminal 1.

terminal 2
cd /path/to/fastrestful
npm run dev -- --port 5055 --host 127.0.0.1

The Vite dev server proxies Django routes (/api/v1, /api/health, /api/schema, /api/docs, /admin, /static, /media) to the backend on port 5056. Public product pages such as /api/{slug} are still served by the frontend.

8. Open the app and sign in

Open these addresses in your browser:

PageAddressWhat it is
Public sitehttp://127.0.0.1:5055Marketplace and marketing site.
Control planehttp://127.0.0.1:5055/admin/Django admin and FastRestful operations.
API docshttp://127.0.0.1:5056/api/docs/Swagger UI for the platform API.
Health checkhttp://127.0.0.1:5056/api/health/readyReadiness for deployment checks.

Sign in to the control plane with admin / Admin12345!.

First-run checklist

  • The public site loads and the marketplace catalog is visible.
  • The control plane opens and you can sign in as admin.
  • Site settings, Platform plans, and the site content section open without errors.
  • A consumer account can create an application and an API key.
  • A request through the gateway appears in request logs.

What to do next

Common installation problems

SymptomLikely causeFix
python3.11: command not foundPython 3.11 is not installed or not on your PATH.Install Python 3.11, reopen the terminal, and run the check again.
npm ci fails with an engine errorNode.js is older than 22.13.Install Node 22.13+ and run npm ci again.
ModuleNotFoundError: djangoThe virtual environment is not active.Run source .venv/bin/activate inside backend/.
FIELD_ENCRYPTION_KEY errorThe key is missing or was not pasted correctly.Generate a new Fernet key and paste the full value with no quotes.
Port 5055 or 5056 is already in useAnother process is using the port.Stop the other process, or choose new ports and update FRONTEND_URL, CORS, and CSRF values.
Page loads but API calls failThe backend is not running.Start terminal 1 with runserver and confirm the health URL.
Admin login is rejectedThe demo seed was not run or a different database is in use.Run seed_demo, or create your own account with python3.11 manage.py createsuperuser.
Local reset. If you want to start over on your local machine only, stop the backend, delete backend/db.sqlite3, then rerun migrate and the seed commands. Never delete a production database.
Start here8 min read

Quickstart: your first gateway request

Go from a clean local install to a metered API call. You will subscribe to a demo API, create a key, send a request, and see the usage event and request log.

Before you begin: finish Installation and keep both terminals running. The backend must be on 127.0.0.1:5056 and the frontend on 127.0.0.1:5055.

1. Sign in and choose a workspace

Open http://127.0.0.1:5055 and sign in with admin / Admin12345!, or register a new account. A workspace (organization) groups your applications, keys, subscriptions, and invoices.

The active workspace travels in the X-Organization header for API calls. The browser console sets it automatically. When you call the API yourself, you must include it unless the request uses an API key instead of a browser session.

2. Subscribe to a product

Open the marketplace, choose a product with a live release, and select a plan. The seeded SignalStack product has a live 2.4.0 release and three plans.

If the plan is free, activation is immediate. If the plan is paid, FastRestful opens the configured payment provider. A browser redirect never grants access by itself: the subscription activates only after a signed provider webhook settles the first invoice.

No payment provider yet? Use a free plan for your first request. To sell paid plans, follow Payment gateways, step by step first.

3. Create an application and key

In the consumer console, create an application and then issue an API key. The raw key is shown once, so copy it immediately into a password manager or a local .env file.

raw key shape
fr_live_<random-secret>

# Store it in a secret manager or local .env file.
# Never commit it to a repository or paste it into a public issue.

Scopes are full, execute, or read. Choose the narrowest scope that works. If you lose the key, revoke it and issue a new one; the old key stops working immediately.

4. Send a request through the gateway

Replace fr_live_… with the key you just created. This example uses the seeded SignalStack product and its path alias.

curl via product path alias
curl --request GET \
  --url 'http://127.0.0.1:5055/api/v1/signal-stack/v2/deliveries?limit=10' \
  --header 'X-API-Key: fr_live_…' \
  --header 'Idempotency-Key: quickstart-001'

With product host routing enabled, the same request uses https://{product-slug}.{FASTRESTFUL_API_HOST_SUFFIX}/.... Both front doors share the same subscription, quota, rate limit, idempotency, metering, and request-log pipeline.

FastRestful API Test tab with endpoint list, secure gateway banner, and live request controls
The public API page keeps the tester, endpoint contract, and request context in one tab.

5. Inspect the result

  • Usage event: recorded server-side with status and latency.
  • Monthly counter: incremented for quota enforcement and overage.
  • Request log: redacted request and response previews visible to the consumer and vendor.
  • Dashboard: requests, success rate, spend, latency, top APIs, and recent activity.

Open the consumer dashboard and the request log. You should see the request you just made. If you do not, check the terminal running the backend for a traceback.

6. Publish a release (optional)

In the vendor studio, open a product workbench, import an OpenAPI contract into a draft version, reconcile endpoint docs, run the diff, submit for review, and promote. A live release requires a supported, non-empty OpenAPI contract and an exactly matching endpoint catalog.

For the full lifecycle, read API products & releases.

If the request fails

ResponseWhat it usually meansWhat to do
401The key is missing, revoked, or malformed.Copy the full key again. Do not include quotes or a trailing space.
403The subscription is inactive or the key scope is too narrow.Confirm the plan is active and the key has execute or full scope.
404The method and path do not match the live release.Check the endpoint in the API page and confirm the release is live.
429The plan quota or rate limit was exceeded.Read retry_after and wait, or use a larger plan.
CORS or CSRF error in a browserThe browser origin is not trusted.Add the frontend origin to CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS. Gateway calls using X-API-Key do not use browser cookies.
Start here14 min read

Configuration, without the overwhelm

Most installations only need a small set of settings to run. Start with the minimum below, then add the integrations you actually use. The long tables further down are reference, not a checklist.

Where settings live

PlaceUse it forWhen it applies
backend/.envSecrets, database, Redis, payment keys, SSO, storage.Restart the backend after changing it.
Admin → Payment gatewaysPayment and payout credentials.Overrides matching .env values field by field. No restart.
Admin → Newsletter → Email settingsCampaign email provider and sender identity.Overrides the environment sender for campaigns.
Admin → Site settingsBranding, navigation, host routing, maintenance mode, site behavior.Applies immediately unless the setting is cached or built into the frontend.
Frontend build variablesNEXT_PUBLIC_* values such as the public API URL.Baked at build time. Rebuild the frontend after changing them.

Minimum local settings

After copying .env.example, these are the only values you must change for the local demo:

backend/.env
DJANGO_DEBUG=True
DJANGO_SECRET_KEY=<generated-random-value>
FIELD_ENCRYPTION_KEY=<generated-fernet-key>
DATABASE_URL=sqlite:///db.sqlite3
FRONTEND_URL=http://localhost:5055
CORS_ALLOWED_ORIGINS=http://localhost:5055,http://127.0.0.1:5055
CSRF_TRUSTED_ORIGINS=http://localhost:5055,http://127.0.0.1:5055

Generate the two keys with the commands in Installation → Create the backend configuration file.

Minimum production settings

Production adds PostgreSQL, Redis, a real hostname, and HTTPS origins. Replace every placeholder with your own values.

production backend environment
DJANGO_DEBUG=False
DJANGO_SECRET_KEY=<50-plus-random-characters>
DJANGO_ALLOWED_HOSTS=api.example.com,www.example.com
DATABASE_URL=postgresql://user:password@db-host:5432/fastrestful
REDIS_URL=redis://redis-host:6379/0
FRONTEND_URL=https://www.example.com
CORS_ALLOWED_ORIGINS=https://www.example.com
CSRF_TRUSTED_ORIGINS=https://www.example.com
FIELD_ENCRYPTION_KEY=<fernet-key>
Do not use SQLite in production. When DJANGO_DEBUG=False, FastRestful requires PostgreSQL and a shared Redis URL. See Deployment for the full sequence.

How to change settings safely

  1. Change one integration at a time.

    Configure, test, and verify before moving to the next provider. This makes failures obvious.

  2. Edit backend/.env on the server.

    Do not edit files inside a running container that will be replaced. Keep secrets in your deployment platform's secret store when possible.

  3. Restart the backend and workers.

    Environment changes are read at process start. Restart Gunicorn, Celery worker, and Celery beat after editing .env.

  4. Rebuild the frontend for NEXT_PUBLIC_* changes.

    Frontend public values are compiled into the build. Restarting the frontend is not enough.

  5. Run the provider's test action.

    Use the test checkout, test email, or webhook delivery log before sending live traffic.

Core environment reference

VariableDefaultPurpose
DJANGO_SECRET_KEYdev-only keyMust be 50+ characters in production.
DJANGO_DEBUGTrueSet False in production. Enables secure-cookie, HSTS, and HTTPS behavior.
DJANGO_ALLOWED_HOSTSlocalhost,127.0.0.1,testserverComma-separated hostnames. Include wildcard API hosts when using product subdomains.
DATABASE_URLsqlite:///db.sqlite3SQLite locally; PostgreSQL in production. Production fails without PostgreSQL.
REDIS_URLemptyShared cache and default Celery broker. Required in production.
CELERY_BROKER_URLREDIS_URLOptional separate broker URL.
FRONTEND_URLhttp://localhost:5055Public frontend origin, checkout return base, and tracking fallback. Must be HTTPS in production.
FRONTEND_PREVIEW_URLFRONTEND_URLBase URL used by admin custom-asset previews.
FIELD_ENCRYPTION_KEYemptyFernet key for encrypted API keys and provider secrets. Required in production.
CORS_ALLOWED_ORIGINSlocal 3000/5055/5173Comma-separated browser origins allowed to call the API.
CSRF_TRUSTED_ORIGINSmirrors CORSCredentialed browser origins trusted for state-changing requests.
SECURE_REFERRER_POLICYstrict-origin-when-cross-originReferrer policy header.

Frontend environment

VariablePurpose
NEXT_PUBLIC_API_URLDjango API origin for split deployments. Omit for same-origin paths.
NEXT_PUBLIC_SITE_URLCanonical public frontend origin for metadata, sitemap, robots, and Open Graph URLs.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYStripe publishable key for embedded card UI. Stripe.js is loaded from the CDN; no npm dependency.
FASTRESTFUL_DEV_BACKEND_URLOptional dev proxy target. Defaults to http://127.0.0.1:5056.
FASTRESTFUL_ADMIN_ORIGINSAllowed admin origins for custom-asset iframe previews.

Gateway and host routing

VariableDefaultPurpose
GATEWAY_TIMEOUT_SECONDS15Upstream request timeout.
GATEWAY_MAX_BODY_BYTES262144Maximum inbound body size.
GATEWAY_MAX_UPLOAD_BYTES5242880Combined multipart upload cap.
GATEWAY_MAX_RETRIES1Retry budget for safe transient failures.
GATEWAY_RETRY_BACKOFF_SECONDS0.05Delay between retries.
GATEWAY_IDEMPOTENCY_TTL_SECONDS86400Replay cache lifetime.
GATEWAY_CIRCUIT_FAILURE_THRESHOLD3Failures before the circuit opens.
GATEWAY_CIRCUIT_WINDOW_SECONDS60Failure-count window.
GATEWAY_CIRCUIT_COOLDOWN_SECONDS30Open-circuit cooldown.
GATEWAY_TRUSTED_PROXY_CIDRSemptyTrusted proxies allowed to provide client IP headers.
GATEWAY_MEDIA_PREVIEW_BYTES1500000Max base64 media preview captured for the console.
FASTRESTFUL_API_HOST_SUFFIXemptyEnables {product}.{suffix} direct routing. Empty keeps the path alias only.

Authentication and SSO

VariablePurpose
SSO_ISSUEROIDC issuer URL. Unset hides the OIDC button.
SSO_CLIENT_ID / SSO_CLIENT_SECRETOIDC client credentials.
SSO_SAML_ENABLEDEnable SAML 2.0 SP endpoints. Requires lxml.
SSO_SAML_ENTITY_IDSAML service-provider entity ID. Default fastrestful.
SSO_SAML_SSO_URLIdentity-provider SSO URL.
SSO_SAML_IDP_CERTIdentity-provider X.509 certificate PEM.
SSO_SAML_AUDIENCEOptional audience restriction.
ORGANIZATION_INVITE_DAYSInvite lifetime in days. Default 7.

Email and newsletter

VariablePurpose
EMAIL_BACKENDConsole backend in debug; SMTP backend in production by default.
EMAIL_HOST / EMAIL_PORTSMTP host and port.
EMAIL_HOST_USER / EMAIL_HOST_PASSWORDSMTP credentials.
EMAIL_USE_TLSEnable STARTTLS. Default true.
ALERT_FROM_EMAILDefault sender and alert identity.
NEWSLETTER_TRACKING_BASE_URLPublic origin for tracking pixels, click redirects, and unsubscribe links. Falls back to FRONTEND_URL.

Newsletter SMTP settings can also be stored in the newsletter studio and override the environment sender for campaigns. See Newsletter studio.

Payments and payouts reference

Looking for setup instructions? The variables below are only the reference. For click-by-click provider dashboard steps, webhook events, PayPal webhook verification, and test checkouts, use Payment gateways, step by step.
Variable groupVariables
StripeSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, optional STRIPE_API_BASE
PayPalPAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_ID, PAYPAL_ENV, optional PAYPAL_API_BASE
FlutterwaveFLUTTERWAVE_PUBLIC_KEY, FLUTTERWAVE_SECRET_KEY, FLUTTERWAVE_WEBHOOK_SECRET, optional FLUTTERWAVE_API_BASE
PaystackPAYSTACK_PUBLIC_KEY, PAYSTACK_SECRET_KEY, PAYSTACK_WEBHOOK_SECRET, optional PAYSTACK_API_BASE
RazorpayRAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_WEBHOOK_SECRET, optional RAZORPAY_API_BASE
PayoutsPAYOUT_PROVIDER optional comma-separated allowlist: stripe, paypal

Admin → Payment gateways stores the same credentials in the database. A database record overrides the environment field by field, and disabling it turns the provider off everywhere even if environment keys remain set.

Media and spec storage

VariablePurpose
MEDIA_STORAGElocal or s3.
MEDIA_S3_BUCKET / MEDIA_S3_REGIONS3 bucket and region.
MEDIA_S3_ENDPOINT_URLCustom S3-compatible endpoint.
MEDIA_S3_ACLOptional direct-S3 ACL. Omit when using CloudFront with OAC.
MEDIA_CDN_DOMAINCloudFront or other CDN domain.
MEDIA_CDN_DISTRIBUTION_IDEnables per-asset purge actions.
SPEC_STORAGEdb or s3 for OpenAPI contract blobs.
SPEC_S3_BUCKET / SPEC_S3_PREFIX / SPEC_S3_REGIONSpec blob destination and prefix. S3 mode needs boto3.

SHORTLINK_BASE_URL sets the public short-link origin, for example https://go.example.com. When empty it falls back to NEWSLETTER_TRACKING_BASE_URL, then FRONTEND_URL. The checklinks command can be scheduled from cron or Celery beat.

Precedence and reloads

  • Environment values provide deployment defaults; admin gateway records override field-by-field.
  • Backend environment changes require a process restart. Some cached settings (maintenance flag, source locale) are cleared automatically on model save.
  • Frontend public variables are baked at build time. Rebuild after changing NEXT_PUBLIC_* values.
  • After changing lib/i18n/*.json, run npm run export:i18n and restart the backend so its cached source bundle is fresh.
Deploy & operate16 min read

Deployment

Move FastRestful from a local SQLite demo to a production deployment with PostgreSQL, Redis, object storage, a WSGI server, TLS, and a static frontend. The platform is designed to be self-hosted.

Before you deploy

  • A domain name and DNS access for the frontend and API hosts.
  • A PostgreSQL 16 database and a Redis 7 instance.
  • HTTPS termination, either at your proxy or at the edge.
  • A generated DJANGO_SECRET_KEY and a backed-up FIELD_ENCRYPTION_KEY.
  • A plan for media storage: local disk is acceptable only for a single-server trial; use S3-compatible storage for real deployments.
  • A backup destination outside the application server.

Recommended order

  1. Deploy the backend and database.

    Get /api/health/ready passing before you touch the frontend.

  2. Deploy the frontend.

    Point it at the API origin and add the frontend origin to CORS and CSRF settings.

  3. Configure email and storage.

    Use External integrations. Verify a test email and a test upload.

  4. Connect payments only when you are ready to sell.

    Use Payment gateways, step by step and complete the test checkout first.

  5. Activate the license and schedule heartbeats.

    Use Licensing & official updates.

  6. Set up backups and monitoring.

    Use Backups & upgrades and Observability.

Recommended topology

production topology
Internet
  │
  ├── app.example.com ──► frontend (static/CDN or Node runtime)
  │                           │
  │                           └── /api/* ──► reverse proxy ──► Gunicorn / Django
  │
  ├── api.example.com ──► reverse proxy ──► Gunicorn / Django
  ├── *.api.example.com ──► wildcard product gateway
  │
  ├── PostgreSQL 16 (primary)
  ├── Redis 7 (cache + Celery broker)
  ├── S3 + CloudFront (media, optional specs)
  └── Celery worker + beat (billing, moderation, email, availability)

You can serve the frontend and API from one origin or split them. If split, set NEXT_PUBLIC_API_URL to the API origin, add the frontend origin to CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS, and set FRONTEND_URL to the public frontend origin.

Docker Compose

The repository includes a Compose file with PostgreSQL, the API, Redis, and optional worker/beat profiles.

Compose runs the backend stack, not the frontend. Build the frontend separately and host it on your static host or CDN, or add a frontend service to your own Compose file. The API container runs migrations and Gunicorn on port 8000.
shell
export DJANGO_SECRET_KEY="$(openssl rand -base64 64 | tr -d '\n')"
export FIELD_ENCRYPTION_KEY="$(python3.11 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')"
export DJANGO_ALLOWED_HOSTS="api.example.com,.api.example.com"
export CORS_ALLOWED_ORIGINS="https://app.example.com"
export CSRF_TRUSTED_ORIGINS="https://app.example.com"
export FRONTEND_URL="https://app.example.com"
export POSTGRES_PASSWORD="$(openssl rand -hex 24)"

docker compose up --build -d
docker compose --profile scheduler up --build -d

Run migrations and seeds inside the API container after the database is healthy:

shell
docker compose exec api python manage.py migrate --noinput
docker compose exec api python manage.py seed_demo
docker compose exec api python manage.py seed_apis
docker compose exec api python manage.py seed_docs
docker compose exec api python manage.py collectstatic --noinput
Change the seed password immediately for any internet-facing deployment. Do not run demo seeds in production unless you intend to keep demo data.

Manual production setup

Backend

shell
cd /srv/fastrestful/backend
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install gunicorn>=22.0 boto3 django-storages  # optional: S3 media

cp .env.example .env
# Fill production values, then:
python3.11 manage.py migrate --noinput
python3.11 manage.py collectstatic --noinput
python3.11 manage.py check --deploy

Gunicorn + systemd

/etc/systemd/system/fastrestful.service
[Unit]
Description=FastRestful Django API
After=network.target postgresql.service redis.service

[Service]
User=fastrestful
Group=fastrestful
WorkingDirectory=/srv/fastrestful/backend
EnvironmentFile=/srv/fastrestful/backend/.env
ExecStart=/srv/fastrestful/backend/.venv/bin/gunicorn \
  config.wsgi:application \
  --bind 127.0.0.1:8000 \
  --workers 3 \
  --timeout 60
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
shell
sudo systemctl daemon-reload
sudo systemctl enable --now fastrestful
sudo systemctl status fastrestful

Nginx reverse proxy

/etc/nginx/sites-available/fastrestful
server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    client_max_body_size 8m;

    location /static/ {
        alias /srv/fastrestful/backend/staticfiles/;
        expires 30d;
        access_log off;
    }

    location /media/ {
        alias /srv/fastrestful/backend/media/;
        expires 7d;
        access_log off;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 65s;
    }
}

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

Wildcard product host

/etc/nginx/sites-available/fastrestful-products
server {
    listen 443 ssl http2;
    server_name *.api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;  # product slug comes from this host
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Add a wildcard DNS record, issue a wildcard certificate with a DNS-01 challenge, and include the wildcard host in DJANGO_ALLOWED_HOSTS. Set FASTRESTFUL_API_HOST_SUFFIX=api.example.com.

Frontend build and hosting

shell
export NEXT_PUBLIC_API_URL="https://api.example.com"
export NEXT_PUBLIC_SITE_URL="https://app.example.com"
export NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_live_..."
npm run build

Deploy the build output to your static host or Node-compatible runtime. The build is self-contained and does not require a managed site control plane. If you use Cloudflare, bind D1/Images only when the deployment needs those features.

Static and media files

  • Static: run collectstatic, serve /static/ from Nginx or your CDN.
  • Local media: serve /media/ from Nginx for small deployments.
  • Production media: use S3 + CloudFront with OAC, keep the bucket private, set MEDIA_STORAGE=s3, and do not set MEDIA_S3_ACL unless serving directly from S3.
  • Specs: leave SPEC_STORAGE=db for a small install; use S3 for larger contract archives.

Health checks

ProbeExpected
GET /api/health/liveProcess liveness. No database dependency.
GET /api/health/readyDatabase + shared cache readiness. Returns 503 with the failing dependency.
GET /api/schema/OpenAPI schema. Useful for deployment smoke checks.

Upgrades and backups

safe upgrade sequence
# 1. Back up PostgreSQL and media
pg_dump --format=custom "$DATABASE_URL" > fastrestful-$(date +%F).dump

# 2. Pull code and install dependencies
git pull --ff-only
pip install -r backend/requirements.txt
npm ci

# 3. Apply migrations and rebuild assets
python3.11 backend/manage.py migrate
npm run build

# 4. Restart API and workers
sudo systemctl restart fastrestful
sudo systemctl restart fastrestful-worker fastrestful-beat

# 5. Verify
curl -f https://api.example.com/api/health/ready

Production gates

  • DJANGO_DEBUG=False, 50+ character secret, valid Fernet key.
  • PostgreSQL, Redis, HTTPS origins, and explicit allowed hosts.
  • Admin credentials changed; demo users removed or disabled.
  • Payment and payout webhooks registered with the correct signed secret.
  • SMTP and provider credentials tested; sender domain authenticated.
  • S3/CloudFront configured if media uploads are enabled.
  • Scheduler worker + beat running or cron equivalents installed.
  • Backups tested by restoring into a separate environment.
  • Monitoring covers readiness, 5xx rate, gateway latency, webhook failures, and queue depth.
  • Legal pages, terms, privacy, and support contact reviewed by the operator.
Deploy & operate10 min read

Operations

Scheduled settlement, notification delivery, availability checks, telemetry retention, health probes, and the operational commands that keep FastRestful healthy.

Celery schedule

TaskSchedulePurpose
settle_period1st of each month, 00:05 UTCGenerate invoices and payouts for the previous period.
escalate_moderation_casesEvery 15 minutesEscalate overdue moderation and appeal cases.
deliver_notification_email_queueEvery 15 minutesDeliver queued notification emails with stale-claim recovery.
schedule_daily_api_availability_checksDaily, 00:20 UTCDispatch product availability checks.
shell
celery -A config worker -l info -Q billing,celery
celery -A config beat -l info

Without a broker, Celery runs eagerly in-process. In production, set REDIS_URL or CELERY_BROKER_URL and run both worker and beat as separate services.

Operational commands

CommandWhat it does
generate_invoices --period YYYY-MMIdempotent invoice generation for a period.
generate_payouts --period YYYY-MMIdempotent payout generation, refunds and fees applied.
prune_telemetryRetention for request logs and usage events.
check_api_availabilityRun product health checks manually.
escalate_moderationEscalate overdue cases without Celery beat.
deliver_notification_emailsDrain the queued notification email backlog.
checklinksRe-check short-link destinations for broken URLs.
shell
python3.11 manage.py generate_invoices --period 2026-10
python3.11 manage.py generate_payouts --period 2026-10
python3.11 manage.py prune_telemetry --request-log-days 90 --usage-event-days 365
python3.11 manage.py check_api_availability
python3.11 manage.py checklinks --limit 200 --timeout 6

Maintenance mode

Enable Platform operations → Maintenance mode in Site settings. The frontend returns a branded 503 page, customer API and gateway calls return a machine-readable maintenance_mode response with Retry-After: 300, and docs/Swagger are paused.

Staff admin, health probes, identity-provider callbacks, and signed payment/payout webhooks stay available so the platform can recover safely and providers are not dropped.

Monitoring

  • Readiness probe for database + shared cache.
  • API 5xx rate and p95 latency by namespace.
  • Gateway upstream error rate and circuit-open events.
  • Webhook failure queue and payout exceptions.
  • Celery queue depth and beat heartbeat.
  • Email bounce/complaint rates and unsubscribe rate.
  • Media storage growth and CDN cache hit ratio.
  • PostgreSQL connections, slow queries, disk, and backup success.

Backups

DataBackup strategy
PostgreSQLNightly logical dump plus provider continuous backups. Test restore quarterly.
S3 mediaEnable bucket versioning and cross-region replication if required.
Specs in DBIncluded in PostgreSQL backups.
Environment secretsStore in a secret manager; back up with access controls and rotation history.
Audit evidenceRetain according to policy; export jobs can create immutable archives.

Scaling notes

  • Run multiple Gunicorn/Django instances behind a load balancer; keep sessions and cache on Redis.
  • Run Celery workers separately from web processes and scale billing and default queues independently.
  • Gateway rate limits use the shared cache; a cache outage returns a retryable 503 rather than failing open.
  • Use PostgreSQL connection pooling if worker counts grow.
  • Move media to S3/CloudFront before traffic makes local disk lifecycle painful.
  • Keep telemetry retention explicit; request logs and usage events are not intended to grow forever.
Deploy & operate12 min read

Security

Security-critical behavior in FastRestful: key custody, tenant isolation, gateway SSRF protection, browser origin policy, secret storage, audit evidence, and the production hardening checklist.

API key custody

  • Raw keys are returned exactly once on create or rotate. They are never rendered again by serializers or admin.
  • Stored forms: a lookup prefix, a SHA-256 hash for constant-time comparison, and a Fernet-encrypted copy for server-side gateway use.
  • Rotation revokes the old key and issues a new one in one call. A revoked key cannot be used to issue replacements.
  • Scopes narrow access: full, execute, or read. Updates can only narrow a scope, never widen it.
  • A key is bound to its application and subscription; it cannot cross applications or workspaces.

Tenant isolation

  • X-Organization selects the active tenant. Server-side permissions recheck membership for every object.
  • Vendor reads are dual-scoped: a vendor sees its own products, subscriptions, invoices, and logs; a consumer sees only its own workspace.
  • Ownership fields are immutable through the API.
  • Public serializers are explicit, not whitelists of a private model. Operational metrics and private fields stay out of anonymous responses.
  • Superusers can read across tenants for support, but writes still require the correct owning scope except explicit platform operations.

Gateway security

  • Upstreams must use HTTPS, resolve to public addresses, stay on the original host, and avoid traversal patterns.
  • Redirects are not followed. DNS answers are checked and the selected public IP is pinned through the TLS connection to prevent rebinding.
  • Timeouts, body caps, upload caps, and bounded retries are enforced before forwarding.
  • Vendor secrets are attached server-side only; they never reach the browser.
  • Idempotency keys are stored as token hashes with a request fingerprint and redacted response, never raw request bodies.
  • Response previews are length-capped and redact Set-Cookie, Authorization, and X-API-Key.
  • A shared-cache outage returns a retryable 503 before quota reservation; the gateway does not fail open.

Browser and origin security

  • Production enforces HTTPS, HSTS, secure cookies, and a strict referrer policy.
  • CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS must name the public frontend exactly.
  • CSRF cookies are readable by the frontend because the shared Django session is used for admin and console writes; never expose them cross-origin.
  • Custom CSS/JS assets are trusted operator content. Use a CSP nonce, preview before activation, and review revision history.
  • Maintenance mode keeps provider callbacks and identity-provider routes available.

Secret management

SecretStorage
DJANGO_SECRET_KEYSecret manager or environment file readable only by the service user. Rotate deliberately.
FIELD_ENCRYPTION_KEYSecret manager. Back it up: losing it makes encrypted keys and provider secrets unrecoverable.
Provider keysAdmin gateway records or environment. Prefer admin records for operational rotation without a deploy.
SMTP credentialsNewsletter studio or environment. Use a dedicated sending identity.
Database URLSecret manager. Use TLS and a least-privilege database role.

Authentication hardening

  • Enable required two-factor for staff and optionally for all accounts in Site settings.
  • Use OIDC or SAML for enterprise identity; password login can remain as a fallback.
  • Keep access tokens short-lived (15 minutes) and rotating refresh tokens blacklisted after rotation.
  • Invites are hashed, single-use, email-bound, and expire after seven days.
  • Admin access requires is_staff; workspace admin is not a platform staff role.
  • Password-reset responses do not reveal whether an account exists.

Audit and evidence

  • Admin actions, moderation decisions, billing state changes, payout reconciliation, settings changes, and account recovery produce audit events.
  • Provider event receipts are append-only and reject payload conflicts, making retries safe.
  • Ledger entries are append-only. Refunds after payout create clawback and fee-reversal entries.
  • Audit export jobs support retention and review without direct database access.

Production checklist

shell
DJANGO_DEBUG=0 \
DATABASE_URL=postgresql://… \
REDIS_URL=redis://… \
FRONTEND_URL=https://app.example.com \
DJANGO_ALLOWED_HOSTS=api.example.com,.api.example.com \
CORS_ALLOWED_ORIGINS=https://app.example.com \
CSRF_TRUSTED_ORIGINS=https://app.example.com \
DJANGO_SECRET_KEY=<50+ chars> \
FIELD_ENCRYPTION_KEY=<fernet key> \
python3.11 manage.py check --deploy
  • Demo accounts and seed data removed from production.
  • Two-factor required for staff; SSO tested end to end.
  • Payment webhooks verified with the provider’s signed secret.
  • Media bucket private; CDN OAC configured; no wildcard write access.
  • Backups encrypted and restore-tested.
  • Logs exclude secrets and redact consumer payloads.
  • Dependency and container images patched on a schedule.
  • Incident contact and vulnerability disclosure path published.
Deploy & operate16 min read

Licensing & official updates

FastRestful is licensed source code sold through Dakidarts. Connect the purchase key to LicenseHub to verify signed official releases, install a newer version, or roll back to an older one. The running gateway keeps working if LicenseHub is temporarily unreachable.

What the update center does

TaskWhere
Connect the purchase license and see entitlement datesAdmin → Software updates (/admin/updates/)
Check, download, verify, and install releasesThe same page, or the fastrestful_update command
Renew the license lease on a schedulepython3.11 manage.py license_heartbeat
See update history and installer logsDownload & install activity on the update page
See license status on the operations overviewAdmin → Operations overview
Before you update: take a database and media backup. The updater snapshots source and SQLite by default, but a verified backup is still your safety net. See Backups & upgrades.

Part 1 — Activate your license

  1. Buy FastRestful and open the Dakidarts dashboard.

    Go to Dashboard → Licenses and copy the full key. It starts with DK-FRST-….

  2. Open the update center.

    Sign in as staff and open https://your-domain/admin/updates/. The sidebar calls it Software updates.

  3. Paste the license key.

    Use the full key, including any dashes. It is encrypted at rest and only a masked form is shown after activation.

  4. Confirm the LicenseHub URL.

    The default is https://dakidarts.com. Keep it unless Dakidarts gives you a different origin. HTTPS is required outside local debug.

  5. Choose the environment.

    Production consumes a production seat. Staging and Development consume non-production seats. Pick the one that matches this installation.

  6. Optionally add the production domain.

    For example api.example.com. This helps you identify the installation in the customer portal.

  7. Choose a release channel.

    Start with Stable. Use Beta or Preview only on a disposable test environment.

  8. Click Activate license.

    Activation creates or reuses a persistent installation_id and binds one seat. The page then shows the edition, update/support windows, activation count, and last heartbeat.

If activation fails, the error is shown on the page. The most common causes are an expired key, a seat limit, or a key copied with a trailing space.

Part 2 — Install an update from the admin

  1. Take a backup.

    Back up PostgreSQL, media, .env, and any custom files. Confirm the backup restores before you need it.

  2. Open Admin → Software updates.

    The page shows the running version, installed version, and connection status.

  3. Click Check for updates.

    LicenseHub returns every release your update window covers: newer, current, and older versions.

  4. Click Download & stage on the target version.

    FastRestful downloads every artifact, verifies the SHA-256, verifies the Ed25519 manifest signature, and extracts the bundle into FASTRESTFUL_UPDATE_DIR/<version>/bundle/.

  5. Click Install.

    The admin starts a detached worker so migrations or a build cannot exceed the web request timeout. The page refreshes while it runs.

  6. Wait for “installed” and the restart banner.

    The running process still uses the old code until you restart. The page shows Installed … · restart required.

  7. Restart the services.

    Restart Gunicorn or your app server, the Celery worker and beat, and the frontend process if it runs on the same host. Use your service manager, not the admin page. Example systemd commands:

    systemd example
    sudo systemctl restart fastrestful
    sudo systemctl restart fastrestful-worker fastrestful-beat
    # restart your frontend process too if it runs on this host
  8. Verify.

    Confirm the version in the update center, open /api/health/ready, and make one test request through the gateway.

Large bundles: the Download & stage action runs inside the admin request. For a large release on a slow link, use the CLI download command from the server shell so a web timeout cannot interrupt it.

Part 3 — Update from the command line

Use this when the admin is unavailable or when you want the install in a terminal session.

shell
cd /srv/fastrestful/backend
source .venv/bin/activate

python3.11 manage.py fastrestful_update status
python3.11 manage.py fastrestful_update check
python3.11 manage.py fastrestful_update download --release-version 1.2.0
python3.11 manage.py fastrestful_update install --release-version 1.2.0

The CLI install is synchronous and prints the captured installer log. The admin install is asynchronous. Both verify the same signed artifacts before running the installer.

Keep the license lease fresh

The certificate lease is 24 hours. A heartbeat every six hours leaves room for transient outages. A daily check is enough for release discovery.

cron example
17 */6 * * * cd /srv/fastrestful/backend && /srv/fastrestful/.venv/bin/python manage.py license_heartbeat --check-updates >> /var/log/fastrestful-license.log 2>&1

A failed heartbeat records the error in the admin but does not stop the gateway.

Part 4 — Roll back to an older version

  1. Open the release list.

    Older releases covered by your update window appear alongside the current version.

  2. Click Install older version on the target release.

    The update center marks the installation with that version. This is a normal release install, not a destructive reset.

  3. Restart the services.

    The restarted process picks up the older source tree.

  4. Check the database.

    If the newer release applied an irreversible migration, restore the pre-update database backup. Keep the database backup and the source backup together.

Source snapshots live in FASTRESTFUL_UPDATE_DIR/backups/. SQLite is copied; PostgreSQL is dumped with pg_dump when available. A rollback across an irreversible migration may still need the database restore.

Part 5 — Channels and environments

ChannelUse it for
stableProduction releases. This is the default.
betaPreview releases for a staging environment.
previewEarly builds. Use only with a disposable environment.

The channel is stored on the connection and sent with every check. Change it on the update page or during activation.

Part 6 — Docker and image-based deployments

The default installer updates a source checkout. In a container, filesystem changes are not durable when the container is recreated. Choose one of these instead:

  • Build and deploy a new image from the downloaded release in your normal pipeline.
  • Set a custom FASTRESTFUL_UPDATE_INSTALL_SCRIPT that pulls the new image or triggers your platform rollout.
  • Use fastrestful_update download to fetch and verify the release, then apply it outside the running container.
Never mount the update directory over application source files. The updater writes bundles and backups into that directory; overlaying source can corrupt both.

Part 7 — Configuration

VariableDefaultPurpose
FASTRESTFUL_VERSION1.0.0Version shown to LicenseHub and compared with releases. Official release builds set this.
DAKIDARTS_LICENSEHUB_URLhttps://dakidarts.comLicenseHub origin for activation, heartbeats, and checks.
FASTRESTFUL_UPDATE_DIR<repo>/updatesPrivate staging directory for artifacts, bundles, and backups.
FASTRESTFUL_UPDATE_TIMEOUT_SECONDS900Maximum installer runtime.
FASTRESTFUL_UPDATE_INSTALL_SCRIPTinstall.shInstaller path relative to the extracted bundle.
FIELD_ENCRYPTION_KEYrequired in productionEncrypts the purchase key. Back it up. Losing it requires reconnecting the license.

Part 8 — Troubleshooting

SymptomWhat to check
license_key_requiredThe key is missing or was cleared. Reconnect from /admin/updates/.
activation_not_foundThe installation was deactivated in the customer portal or replaced. Reconnect or re-activate.
updates_expiredRenew the update entitlement in the Dakidarts portal. The existing installation keeps running.
activation_limitDeactivate an old installation in the Dakidarts portal, then retry.
Download checksum failureRetry. Check for a proxy or cache corrupting artifacts. Do not run the bundle.
No install scriptThe release bundle is incomplete. Use your deployment workflow or rebuild the release with scripts/install-release.sh.
Install succeeded but the version did not changeRestart the backend, workers, and frontend. The running process still uses the previous code.
Health checks fail after an updateRead the update log, then restore the previous source backup in FASTRESTFUL_UPDATE_DIR/backups/ and the matching database backup if needed.

Part 9 — Security notes

  • Only staff with licensing.change_licenseconnection can open the update center.
  • CSRF protection is enforced on every action.
  • The purchase key is encrypted at rest and is never rendered after activation.
  • Downloads use short-lived LicenseHub tokens. Artifacts are verified by SHA-256 and the Ed25519 manifest signature before extraction or execution.
  • Archive extraction rejects absolute paths, traversal, symlinks, and hardlinks.
  • Keep the update directory private. Do not serve it from Nginx or object storage.
  • A LicenseHub outage or expired update window does not take a running gateway offline. Revocation blocks official downloads; it does not remotely stop an existing perpetual installation.

For release maintainers

Official releases are private LicenseHub artifacts. A bundle may contain one source archive or several artifacts (source, migrations, frontend, docs). All artifacts are extracted into the same bundle/ directory in the order returned by LicenseHub.

The bundle must include an installer at one of these paths:

  • install.sh
  • scripts/install.sh
  • scripts/install-release.sh
  • the path in FASTRESTFUL_UPDATE_INSTALL_SCRIPT

scripts/install-release.sh in this repository is the reference implementation. Copy it to the root of the release bundle as install.sh. It snapshots the previous source and database, overlays the new source, preserves local secrets/media/database files, installs backend dependencies, runs Django migrations, and prints the restart requirement.

The installer receives FASTRESTFUL_RELEASE_DIR, FASTRESTFUL_BUNDLE_DIR, FASTRESTFUL_UPDATE_DIR, FASTRESTFUL_APP_ROOT, FASTRESTFUL_FROM_VERSION, FASTRESTFUL_TO_VERSION, FASTRESTFUL_PYTHON, and FASTRESTFUL_INSTALL_LOG. It runs with bash, a timeout, and a captured log. It must be non-interactive and leave the installation recoverable if it fails.

Deploy & operate8 min read

Troubleshooting

The failures teams hit most often — and the checks that resolve them.

API returns 401 or 403

  • 401: missing/expired bearer token, missing X-API-Key, or a key that does not resolve. Refresh the JWT or issue a new key.
  • 403: authenticated but not allowed. Check workspace membership, role, subscription status, and endpoint entitlement.
  • External gateway calls should use X-API-Key (or a bearer fr_live_… key), not a dashboard JWT.
  • Dashboard calls need both Authorization: Bearer <access> and X-Organization: <uuid>.

Browser requests fail with CORS or CSRF

  • Add the exact frontend origin to CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS.
  • Use the same scheme, host, and port that the browser uses. localhost and 127.0.0.1 are different origins.
  • Rebuild the frontend after changing NEXT_PUBLIC_API_URL or NEXT_PUBLIC_SITE_URL.
  • For gateway calls from arbitrary consumer origins, configure CORS regexes or permissive CORS only on the product API host.

Gateway request fails

SymptomCheck
429Monthly quota or per-minute rate limit reached. Inspect subscription usage and plan limits.
409Idempotency key reused with different request input. Use a new key or repeat the original request.
503 retryableShared cache unavailable, circuit open, or maintenance mode. Check Redis and platform availability.
Upstream TLS/DNS failureConfirm the vendor upstream is public HTTPS and resolves without redirects.
Key rejectedKey is revoked, expired, bound to another application, or its subscription is not active.
Endpoint not foundMethod + path must match an endpoint in the live release. Check the release and plan entitlement.

Migrations fail or the schema is behind

shell
python3.11 manage.py showmigrations
python3.11 manage.py makemigrations --check --dry-run
python3.11 manage.py migrate --noinput
python3.11 manage.py check

If a migration conflicts with a custom branch, resolve it on a copy of the database and never edit an already-applied migration in production.

Media returns 404 or a stale CDN asset

  • Local mode: confirm MEDIA_ROOT is writable and Nginx serves /media/.
  • S3 mode: verify bucket permissions, region, endpoint, and CDN origin. Keep the bucket private when using OAC.
  • After replacing an asset, purge the CDN distribution or use the admin purge action.
  • Confirm MEDIA_CDN_DOMAIN has no trailing path and matches the certificate.

Payment webhooks are failing

Start with the provider-specific setup in Payment gateways, step by step. Then check these in order:

  1. The URL is exact and public. Register /api/v1/billing/stripe-webhook/, /paypal-webhook/, /flutterwave-webhook/, /paystack-webhook/, or /razorpay-webhook/ on the same hostname users reach. Localhost will not work.
  2. Test and live values are not mixed. A test key with a live webhook ID (or the reverse) fails every delivery.
  3. The secret is the right kind of secret. Stripe needs the endpoint signing secret (whsec_…), not the API secret key. Flutterwave needs the secret hash you created. Razorpay needs the webhook signing secret.
  4. PayPal uses a webhook ID, not a secret. Put the ID from the same PayPal app and environment in PAYPAL_WEBHOOK_ID. FastRestful verifies each event with PayPal's signature-verification API. A missing or wrong ID returns Bad signature. See PayPal webhook troubleshooting.
  5. Paystack usually needs no webhook secret. Leave PAYSTACK_WEBHOOK_SECRET blank; FastRestful verifies with PAYSTACK_SECRET_KEY.
  6. The event types are selected. A webhook that is reachable but not subscribed to the settlement event leaves the invoice open.
  7. Your proxy preserves provider headers. Nginx, Cloudflare, and load balancers must forward the signature headers unchanged.
  8. The amount and currency match. FastRestful rejects a settlement that does not match the invoice.
  9. Check the receipt. Open Admin → Payment events; duplicate event IDs are acknowledged without double-settling. Resend the failed event from the provider dashboard.

Email is not sending

  • Development uses the console email backend. Check the terminal output.
  • Production uses SMTP by default. Test host, port, TLS, username, and password.
  • Newsletter settings can override the environment sender; confirm the active provider in the studio.
  • Check SPF, DKIM, and DMARC before blaming the queue. Bounce and complaint rates suppress future sends.

Frontend build or route issues

  • Run npm run typecheck and npm test before debugging deployment.
  • A blank API-driven page usually means the API origin is unreachable or CORS is incomplete.
  • If public product pages work but /api/{slug} 404s, confirm the proxy only forwards Django namespaces and not the frontend-owned product route.
  • After i18n changes, run npm run export:i18n and restart Django.

Locale edits do not show up

  • Bundled French loads from the frontend bundle; admin overlays come from the active locale API.
  • Save the admin overlay, confirm the locale is active, and hard-refresh the browser.
  • After changing fr.json, export the backend source and restart the API to clear its process cache.
  • Check for a stale localStorage locale preference.
Platform10 min read

Authentication & access

FastRestful uses short-lived JWT access tokens, rotating refresh tokens, and a shared Django browser session. The same sign-in can open the consumer console, vendor studio, and staff admin.

Authentication flows

FlowEndpointResult
Password loginPOST /api/v1/auth/token/Access + refresh token and a Django session.
RegistrationPOST /api/v1/auth/register/User, optional workspace, JWT pair, and session.
RefreshPOST /api/v1/auth/token/refresh/New access token; rotation blacklists the old refresh token.
LogoutPOST /api/v1/auth/logout/Clears the session; optionally blacklists a supplied refresh token.
Password resetPOST /api/v1/auth/password-reset/Non-enumerating reset email.
Reset confirmPOST /api/v1/auth/password-reset/confirm/Single-use token replaces the password.
OIDC/api/v1/auth/sso/status|start|callback/Provider login and exchange.
SAML 2.0/api/v1/auth/saml/status|metadata|start/POST-binding service-provider flow.

Request headers

dashboard API request
Authorization: Bearer <access-token>
X-Organization: <organization-uuid>
Content-Type: application/json
external gateway request
X-API-Key: fr_live_…
# backwards-compatible alternative:
Authorization: Bearer fr_live_…

The dashboard uses JWT plus the explicit active workspace header. External consumers use a subscribed product API key. A dashboard JWT must not be used as a customer API key, and a raw API key must not be sent to dashboard endpoints.

Workspace roles

RoleTypical access
OwnerFull workspace control, billing, team, and vendor operations. Last owner is protected.
AdminTeam, applications, keys, subscriptions, publishing, billing, and support.
DeveloperApplications, keys, endpoint testing, and product publishing where allowed.
BillingInvoices, payment methods, coupons, refunds, and payouts.
ViewerRead-only access to workspace resources.

Platform staff access is separate: it requires is_staff and never follows from a workspace admin role. Staff permissions are enforced per Django model and admin action.

Two-factor authentication

  • TOTP authenticator enrollment with QR/provisioning URI and one-time recovery codes.
  • Recovery codes are shown once, hashed at rest, and regenerated as a set.
  • A superuser can require two-factor for every account in Site settings.
  • Admin access can be gated until enrollment is confirmed; the console shows a dedicated setup gate.

Workspace invites

Owners and admins invite by email and assign a role. Invite tokens are hashed, single-use, email-bound, expire after seven days, and can be resent or revoked. Accepting an invite requires an authenticated user with a matching email and can reactivate an inactive membership.

team API
GET  /api/v1/organization-invites/
POST /api/v1/organization-invites/          {"email": "...", "role": "developer"}
POST /api/v1/organization-invites/{id}/resend/
POST /api/v1/organization-invites/{id}/revoke/
POST /api/v1/auth/invites/accept/           {"token": "..."}

SSO configuration

OIDC

backend/.env
SSO_ISSUER=https://login.example.com/realms/acme
SSO_CLIENT_ID=fastrestful
SSO_CLIENT_SECRET=...
FRONTEND_URL=https://app.example.com

Register the callback URL https://api.example.com/api/v1/auth/sso/callback/ with the identity provider. Safe relative destinations and invitation tokens survive the round trip.

SAML 2.0

backend/.env
SSO_SAML_ENABLED=1
SSO_SAML_ENTITY_ID=fastrestful
SSO_SAML_SSO_URL=https://idp.example.com/sso
SSO_SAML_IDP_CERT="-----BEGIN CERTIFICATE-----..."
SSO_SAML_AUDIENCE=fastrestful

Install lxml before enabling SAML. Metadata is served at /api/v1/auth/saml/metadata/; configure the IdP with the assertion consumer service URL from the start endpoint.

API key lifecycle

  • Create a key in the selected application; the raw key appears once.
  • Send it as X-API-Key or a bearer fr_live_… credential.
  • Rotate to revoke the old key and issue a new one atomically.
  • Revoke immediately when a credential is lost; there is no recovery of the raw value.
  • Use execute or read scope for least privilege where the client does not need full management.
Platform12 min read

API products & releases

A product is the public identity. A version is the immutable contract. Endpoints, documentation, gateway policy, and plan entitlements all travel with that release boundary.

The product model

EntityOwns
CategoryMarketplace navigation and grouping.
ApiProductPublic name, slug, logo, category, vendor, upstream, visibility, website, health check.
ApiVersionVersion number, changelog, OpenAPI contract, status, release notes.
EndpointMethod, path, summary, parameters, request/response docs, policies, rate limit.
PlanPrice, interval, quota, overage, trial, features, endpoint entitlement.

Create and publish a product

  1. In the vendor studio, create the product with name, slug, category, tagline, README, and upstream URL.
  2. Create a draft version and import an OpenAPI 3.x or Swagger 2.0 contract.
  3. Review the reconciled endpoint catalog and endpoint documentation.
  4. Create at least one public plan and configure quotas, rate limits, and entitlement.
  5. Run publication readiness; all four checks must pass.
  6. Submit the version for review, then promote it live.
  7. Publish the product. The previous live version retires automatically.

OpenAPI import

shell
curl --request POST \
  --url 'https://api.example.com/api/v1/products/signal-stack/import-spec/' \
  --header 'Authorization: Bearer <access>' \
  --header 'X-Organization: <vendor-org-uuid>' \
  --header 'Content-Type: application/json' \
  --data '{
    "version": "2.5.0",
    "openapi": { "openapi": "3.0.3", "paths": { } },
    "changelog": "Add delivery scheduling."
  }'

Import creates or updates a draft version, reconciles endpoint rows from paths, reports created/updated/deleted endpoints, and preserves gateway policy fields on retained operations. Live version import is rejected.

Publication readiness

GateRequirement
Listing metadataName, slug, tagline, category, and README are present.
UpstreamA valid HTTPS upstream is configured.
Live releaseAt least one live version with a supported OpenAPI contract and matching endpoints.
Public planAt least one public plan is available.

Release state machine

state flow
draft ──submit_review──► review ──promote──► live ──deprecate──► deprecated
  └───────────────────────promote────────────────┘

# product visibility
draft ──publish──► public ──unpublish──► draft
                       └──retire──► retired ──restore──► draft
  • Live versions are immutable: edits are rejected at model and serializer level.
  • Promotion retires the previous live version to keep a single-live invariant.
  • The contract and endpoint catalog must match exactly before promotion.
  • Plan endpoint entitlements are remapped by method + path when a new live release replaces the old one.

Breaking-change reports

shell
GET /api/v1/versions/{version_id}/diff/?against={previous_version_id}

# Review added, removed, and changed operations before promotion.
# A release can be promoted only after the contract is validated.

Product host routing

Every published product has two equivalent front doors:

EnvironmentBase URL
Production with wildcard DNShttps://{product-slug}.api.example.com
Single host / developmenthttps://api.example.com/api/v1/{product-slug}

The middleware reads the first host label as the product slug. Path aliases are registered as a trailing route, so named API routes always win. Unknown products return a 404 with a hint; keyless requests to unknown products return 401 to avoid enumeration.

Plan entitlements

  • all grants every public endpoint in the live release.
  • selected grants only the configured endpoint set.
  • Selected entitlements are enforced at the gateway, including raw-path requests that bypass the endpoint catalog.
  • Commercial terms lock after the first subscription; create a replacement plan instead of editing a subscribed plan.
Platform11 min read

Gateway & metering

The gateway is the only path to vendor upstreams. It resolves the product, authenticates the key, enforces subscription and plan policy, reserves quota, forwards safely, meters usage, and writes a redacted trace.

Request pipeline

  1. Resolve the product from the host or path alias and find the live version endpoint by method + path.
  2. Authenticate with a dashboard JWT/session or an external X-API-Key / bearer fr_live_… credential.
  3. Resolve the subscription for the product and application. Active and trialing subscriptions only.
  4. Validate the key binding and stamp last_used_at. A key cannot cross applications or workspaces.
  5. Reserve quota against the monthly counter and enforce per-endpoint rate limits atomically.
  6. Run network safety checks: HTTPS, public DNS, no redirects, no traversal, pinned IP.
  7. Forward with timeout, body caps, upload caps, and bounded retries.
  8. Meter: usage event, monthly counter, redacted request log, and response preview.

Direct product calls

product host
curl --request POST \
  --url 'https://signal-stack.api.example.com/v2/deliveries' \
  --header 'X-API-Key: fr_live_…' \
  --header 'Content-Type: application/json' \
  --data '{"recipient":"[email protected]","template":"welcome"}'
path alias
curl --request GET \
  --url 'https://api.example.com/api/v1/signal-stack/v2/deliveries?limit=10' \
  --header 'X-API-Key: fr_live_…'

The legacy envelope POST /api/v1/gateway/execute/ remains for existing integrations and console tools. New snippets use the direct form.

Gateway headers

HeaderPurpose
X-API-KeySubscribed product key. Recommended for external clients.
Authorization: Bearer fr_live_…Backwards-compatible key form.
Idempotency-KeyReplay-safe key for completed responses, default TTL 24 hours.
X-OrganizationDashboard workspace only; not for external gateway clients.

Quotas and rate limits

  • Monthly quota is reserved before forwarding and counts routed requests, including upstream failures.
  • Unlimited quota removes only the monthly cap; the per-minute limit and endpoint entitlement still apply.
  • Per-endpoint rate limit uses an atomic cache fixed window. A shared-cache outage returns a retryable 503 before quota reservation.
  • Overage is billed per 1,000 requests when configured; zero means alert-only.

Idempotency and retries

  • Completed responses are stored with a SHA-256 token hash, request fingerprint, and redacted response for the configured TTL.
  • Replays return the stored response before quota or upstream calls.
  • Reusing a key with different request input returns 409.
  • Retries are bounded and only apply to safe transient failures; non-idempotent methods are not retried blindly.
  • A circuit breaker opens after repeated upstream failures and returns a retryable 503 during cooldown.

SSRF and network safety

  • Upstreams must be public HTTPS. Private, loopback, link-local, and unsafe DNS answers are rejected.
  • The path must stay on the original host. .. traversal is rejected.
  • Redirects are not followed.
  • The selected public DNS address is pinned through the TLS connection to prevent rebinding.
  • Vendor secrets are attached server-side and never returned to the browser.
  • Response previews are capped and redact cookies, authorization, and API-key headers.

Request logs and metering

  • Consumer view: active-workspace request history with search, status filters, and CSV export.
  • Vendor view: request-level logs across owned products, with consumer, endpoint, latency, status, and redacted previews.
  • Usage events: status code, latency, subscription, product, endpoint, and timestamp.
  • Monthly counters: quota and analytics read model.
  • Retention is configurable with prune_telemetry.

Gateway environment

All timeouts, caps, retry budgets, circuit thresholds, idempotency TTL, trusted proxy CIDRs, media preview limits, and the product host suffix are environment-driven. See Configuration for the complete table.

Platform14 min read

Billing & payouts

Billing has two jobs: let consumers pay for API plans, and let the platform operator charge for workspace capacity. Both use the same providers, invoices, and webhook settlement.

Connecting a provider for the first time? Read Payment gateways, step by step first. It covers the provider dashboard, webhook URLs, PayPal's webhook ID, and test checkouts. This guide explains what happens after the provider is connected.

The two billing planes

Think of it as two separate cash registers:

PlaneWho paysWho receivesWhat it unlocks
Vendor API plansAPI consumerAPI vendor, minus the marketplace feeRequests to that product, subject to its quota and rate limit.
Platform plansWorkspace ownerPlatform operatorWorkspace count and API listing limits on the platform itself.

Both use hosted checkout, signed webhooks, invoices, and reconciliation. Vendor API revenue flows into payout settlement; platform subscription revenue does not.

Vendor plans

  • Monthly or annual interval, currency, price, and description.
  • Monthly quota, per-minute rate limit, overage per 1,000 requests, trial days.
  • Up to eight public feature lines and one recommended public plan.
  • all or selected endpoint entitlement.
  • Commercial terms lock after the first subscription; use a replacement plan for changes.

Checkout and settlement

  1. Consumer selects a plan and accepts API terms.
  2. A pending subscription and first-period invoice are created.
  3. The configured provider opens hosted checkout.
  4. The provider sends a signed webhook after payment.
  5. FastRestful verifies the signature, records the provider event, validates amount/currency, and marks the invoice paid.
  6. Entitlement activates only after settlement. Browser redirects never grant access.
provider webhooks
POST /api/v1/billing/stripe-webhook/
POST /api/v1/billing/paypal-webhook/
POST /api/v1/billing/flutterwave-webhook/
POST /api/v1/billing/paystack-webhook/
POST /api/v1/billing/razorpay-webhook/

Provider support

Every provider below has a step-by-step setup guide in Payment gateways, step by step.

ProviderCheckoutPayoutsCredentials
StripeYes, embedded PaymentIntent or hostedConnect transfersSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
PayPalBilling subscriptionsPayoutsClient ID/secret, webhook ID
FlutterwaveHosted checkoutManual reconciliationPublic/secret/webhook secret
PaystackHosted checkoutManual reconciliationPublic/secret/webhook secret
RazorpayHosted checkoutManual reconciliationKey ID/secret/webhook secret

Admin → Payment gateways overrides environment credentials field by field. Disabling a gateway record turns that provider off everywhere, even if environment keys remain.

Refunds

  • Vendors with billing permission refund paid invoices through the original gateway.
  • Stripe refunds reconcile cumulatively from webhook events; hosted gateways confirm synchronously.
  • Idempotency keys and pending refund requests make retries safe.
  • Full refunds mark the invoice refunded.
  • Refunds after payout append vendor revenue clawback and proportional fee-reversal ledger entries.
  • Consumers cannot self-refund; manual payments must be refunded through their provider.

Payouts

payout state
pending ──► in_transit ──► paid
   │              │
   ├──► failed ◄──┘
   └──► reversed

# Only paid invoices settle. Refunds reduce the next transfer.
# Gross collected revenue is stored separately from net transfer.
# Provider event IDs are unique and retries are idempotent.
  • Configure a vendor destination with PATCH /api/v1/orgs/{id}/payout-provider/ (acct_… for Stripe or email for PayPal).
  • Start a transfer with POST /api/v1/payouts/{id}/transfer/.
  • Reconcile with POST /api/v1/payouts/{id}/reconcile/; strict states require a transfer ID or failure reason.
  • Stripe webhooks reconcile transfer.created|paid|failed|reversed.

Platform subscriptions

Multi-vendor mode must be on. Platform subscription checkout is unavailable while multi-vendor mode is disabled in Site settings. Enable it before selling workspace plans.
  • Platform plans are managed at Admin → Platform plans; maximum five plans.
  • Each plan can define owned-workspace and API listing limits, benefits, and a recommendation.
  • Paid plans stay unpublished until a display amount and matching recurring provider price ID are configured.
  • Checkout and Billing Portal sessions are server-created with fixed FRONTEND_URL destinations.
  • Subscription reconciliation is read-only in admin so staff changes cannot drift from the provider.

Settlement commands

shell
python3.11 manage.py generate_invoices --period 2026-10
python3.11 manage.py generate_payouts --period 2026-10

# Both commands are idempotent and safe to rerun.

The monthly Celery task runs on the 1st at 00:05 UTC. Without a broker, invoke the commands manually.

Provider environment

See Configuration → Payments and payouts for every variable. At minimum: Stripe secret/webhook, PayPal client/webhook, or the hosted-gateway public/secret/webhook trio. Frontend card UI also needs NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY.

Platform24 min read

Payment gateways, step by step

Connect a real payment provider without guessing. Each provider section has two halves: what to click in the provider dashboard, then what to enter in FastRestful. PayPal includes a complete webhook walkthrough, including where to find the webhook ID that FastRestful requires.

Before you start

You needWhy
A public HTTPS URLEvery provider must be able to reach your webhook endpoint. localhost will not work in a real provider dashboard.
A test account firstUse sandbox/test mode until a test payment settles. Never debug with live money.
Admin accessYou will configure providers in Admin → Payment gateways or in backend/.env.
A plan to sellVendor API plans are created in the vendor studio. Platform plans are created in Admin → Platform plans.
One provider at a time. Configure Stripe, run a test payment, and confirm the invoice is paid before you add PayPal or a hosted gateway. Provider bugs are much easier to find when only one thing changed.
Free plans still work without a provider. Paid checkout cannot open until at least one gateway is enabled. If the checkout says “No payment provider is configured,” finish one provider section below first.
Provider dashboards change. Use these steps as a map, not a screenshot. If a menu label differs, look for the same concept: API keys, webhooks, products, prices, or payouts.

How money moves in FastRestful

  1. FastRestful creates an invoice.

    The subscription and first-period invoice exist before checkout opens. Nothing is marked paid yet.

  2. For paid plans, the provider opens checkout.

    The customer pays on the provider's hosted page. FastRestful only stores the checkout reference.

  3. The provider sends a signed webhook.

    FastRestful verifies the signature, records the event once, and checks the amount and currency.

  4. The invoice is settled.

    Only now does the subscription activate and entitlement begin. A browser redirect never grants access.

Webhooks are the source of truth. If the customer pays but the webhook never arrives, the invoice stays open. The provider dashboard is the place to inspect and resend the delivery.

Where credentials go

You can put credentials in backend/.env or in Admin → Payment gateways. Admin records are easier to rotate and override environment values field by field. Disabling an admin record turns the provider off everywhere, even if environment keys still exist.

ProviderPublic fieldSecret fieldWebhook field
Stripe—Secret key (sk_…)Signing secret (whsec_…)
PayPalClient IDClient secretWebhook ID (not a secret and not the URL)
FlutterwavePublic keySecret key (FLWSECK-…)Secret hash you create
PaystackPublic key (pk_…)Secret key (sk_…)Leave blank to use the secret key
RazorpayKey ID (rzp_…)Key secretWebhook signing secret
Admin field names are generic. On the payment gateway form, Public key means the provider's public/client identifier, Secret key means its private API secret, and Webhook secret means the webhook signing value. For PayPal that is Client ID, Client secret, and Webhook ID respectively.

Testing webhooks on your laptop

Providers cannot reach http://127.0.0.1:5056. Use one of these approaches for local testing.

Stripe CLI

  1. Install and log in to the Stripe CLI.

    Follow Stripe's CLI install guide, then run stripe login and approve the browser prompt.

  2. Forward events to your local backend.
    shell
    stripe listen --forward-to localhost:5056/api/v1/billing/stripe-webhook/
  3. Copy the printed signing secret.

    The CLI prints whsec_…. Put it in STRIPE_WEBHOOK_SECRET or the Stripe admin record. Keep the CLI running.

  4. Trigger a test event.
    shell
    stripe trigger checkout.session.completed
    stripe trigger invoice.paid

PayPal, Flutterwave, Paystack, and Razorpay

These providers need a public HTTPS URL. Use a tunnel such as ngrok or Cloudflare Tunnel, then register the tunnel URL in the provider dashboard:

shell
# Example with ngrok
ngrok http 5056

# Use the generated https URL, for example:
# https://abc123.ngrok-free.app/api/v1/billing/paypal-webhook/

Change the tunnel URL later? Update the provider dashboard and the FastRestful webhook destination together. A stale URL is the most common cause of "payment succeeded but the invoice never closed."

PayPal also provides a webhook simulator in the developer dashboard. Point it at the tunnel URL and use the webhook ID from the same sandbox app so FastRestful can verify the simulated event.

Choosing a provider

ProviderCheckoutPayoutsBest fit
StripeHosted checkout or embedded card UIConnect transfersGlobal cards, wallets, and automated vendor payouts.
PayPalPayPal Checkout orders; PayPal Billing for platform plansPayPal PayoutsBuyers who prefer PayPal balances, cards, and PayPal subscriptions.
FlutterwaveHosted checkoutManual reconciliationAfrican markets with local payment methods.
PaystackHosted checkoutManual reconciliationNigeria, Ghana, Kenya, and South Africa.
RazorpayPayment LinksManual reconciliationIndia, with INR-first pricing.
Vendor plans vs platform plans. Vendor API-plan subscriptions are billed through FastRestful invoices: the first period opens a hosted checkout, and later invoices are generated by the settlement job. Platform subscriptions use the provider's recurring billing: Stripe Prices or PayPal Billing plans. Platform subscription checkout also requires multi-vendor mode to be enabled in Site settings. They are configured in different places.

Stripe, step by step

1. Create and activate a Stripe account

  1. Create a Stripe account and complete the business profile.
  2. Keep Test mode enabled while you integrate. The toggle is in the Stripe Dashboard top bar.
  3. You can take test payments before Stripe finishes account activation.

2. Copy the API keys

  1. Open Developers → API keys.

    In the Stripe Dashboard, open Developers and then API keys.

  2. Copy the secret key.

    Use the test secret key first (sk_test_…). Put it in STRIPE_SECRET_KEY or the Stripe admin record. Never put the secret key in frontend code.

  3. Copy the publishable key.

    Put the test publishable key (pk_test_…) in NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY if you want the embedded card UI. Rebuild the frontend after changing it.

3. Add the Stripe webhook endpoint

  1. Open Developers → Webhooks → Add endpoint.
  2. Set the endpoint URL.
    live or staging URL
    https://api.example.com/api/v1/billing/stripe-webhook/
  3. Select the events FastRestful reconciles.
    • checkout.session.completed
    • invoice.paid and invoice.payment_succeeded
    • invoice.payment_failed
    • customer.subscription.created, customer.subscription.updated, customer.subscription.deleted
    • charge.refunded
    • transfer.created, transfer.paid, transfer.failed, transfer.reversed (payouts)
  4. Copy the signing secret.

    Stripe shows a value starting with whsec_…. This is STRIPE_WEBHOOK_SECRET. It is not the API secret key.

Stripe signs every delivery. FastRestful rejects stale signatures and fails closed when STRIPE_WEBHOOK_SECRET is missing, so a correct signing secret is mandatory.

4. Add Stripe in FastRestful

Open Admin → Payment gateways and edit the Stripe row (recommended), or use environment variables.

FastRestful fieldStripe value
ModeTest until the sandbox succeeds, then Live.
Secret keysk_test_… or sk_live_…
Webhook secretwhsec_…

If you use environment variables instead, set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET, then restart the backend and workers.

5. Platform plans and Stripe Prices

Only needed if you sell platform subscriptions. Vendor API plans use the product's own price and do not need a Stripe Price.

  1. Create a Product in Stripe.

    Open Product catalog → Add product. Name it after your platform plan.

  2. Add a recurring Price.

    Use the exact amount, currency, and billing interval your platform plan displays. Monthly and annual prices must be separate Prices.

  3. Copy each Price ID.

    Price IDs start with price_….

  4. Paste them into the platform plan.

    Open Admin → Platform plans and fill Stripe monthly price ID and Stripe annual price ID.

FastRestful checks the Price against the plan amount, currency, and interval at checkout. A mismatch returns 409 with an explanation instead of charging the wrong amount.

6. Stripe Connect for vendor payouts

  1. Enable Connect in Stripe and create a connected account for each vendor you pay.
  2. Copy the connected account ID (acct_…).
  3. Store it as the vendor's destination with PATCH /api/v1/orgs/{id}/payout-provider/ or from the vendor payout settings.
  4. Optionally set PAYOUT_PROVIDER=stripe to allow only Stripe payouts.
  5. Keep transfer.* events selected on the Stripe webhook.

7. Test Stripe

  1. Use test card 4242 4242 4242 4242, any future expiry, any CVC.
  2. Complete a checkout and confirm the invoice becomes paid.
  3. Confirm the subscription becomes active and the consumer key works.
  4. Open Admin → Payment events and confirm the event was processed once.
  5. Trigger a refund and confirm the invoice refund state is correct.

PayPal, step by step

PayPal is the provider with the most platform-side steps. FastRestful uses two different PayPal products: Checkout orders for vendor API-plan purchases and Billing subscriptions for platform plans. Both use the same app credentials and webhook.

1. Create a PayPal app and copy credentials

  1. Open the PayPal Developer Dashboard.

    Go to developer.paypal.com and sign in with your PayPal business account.

  2. Choose Sandbox or Live.

    Use the Sandbox/Live switch at the top. Sandbox and live have separate apps, credentials, and webhook IDs.

  3. Open Apps & Credentials → Create App.

    Give it a name such as FastRestful and select the business account that should receive payments.

  4. Copy the Client ID and Secret.

    These go into PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET (or the PayPal admin record). If the secret is hidden, use the app's regenerate action and copy the new value.

2. Add the PayPal webhook (required)

This is the step most people miss. Without a webhook ID, FastRestful rejects every PayPal event and paid orders stay unpaid.

  1. Open your app in Apps & Credentials.
  2. Scroll to Webhooks and click Add Webhook.

    The exact label may be Webhooks or Add Webhook depending on the PayPal dashboard version.

  3. Enter the webhook URL.
    PayPal webhook URL
    https://api.example.com/api/v1/billing/paypal-webhook/

    The URL must be public HTTPS. A browser redirect is not proof of payment; PayPal posts signed events to this URL, and those events settle the invoice.

  4. Select the event types.
    Event groupSelectWhy FastRestful needs it
    Checkout ordersCHECKOUT.ORDER.APPROVED, PAYMENT.CAPTURE.COMPLETEDCaptures and settles vendor API-plan payments.
    SubscriptionsBILLING.SUBSCRIPTION.ACTIVATED, BILLING.SUBSCRIPTION.CANCELLED, BILLING.SUBSCRIPTION.SUSPENDED, BILLING.SUBSCRIPTION.EXPIREDActivates and updates platform subscriptions. ACTIVATED is the event that grants entitlement.
    Subscription paymentsPAYMENT.SALE.COMPLETEDRecords renewals and reactivates past-due platform subscriptions.
    PayoutsPAYMENT.PAYOUTS-ITEM.SUCCEEDED, PAYMENT.PAYOUTS-ITEM.FAILED, PAYMENT.PAYOUTS-ITEM.BLOCKED, PAYMENT.PAYOUTS-ITEM.RETURNED, PAYMENT.PAYOUTS-ITEM.CANCELEDReconciles vendor payout transfers.
  5. Save the webhook and copy the Webhook ID.

    Open the saved webhook if needed. PayPal shows a webhook ID (a short alphanumeric string). This is PAYPAL_WEBHOOK_ID. It is not the webhook URL and not the client secret.

Why PayPal is different. FastRestful does not hash the PayPal payload with a shared secret. It sends the webhook headers and event body to PayPal's /v1/notifications/verify-webhook-signature endpoint together with the stored webhook ID. If the ID is missing, from the other environment, or from another app, verification fails and FastRestful returns 400 Bad signature. This is intentional fail-closed behavior.

3. Add PayPal in FastRestful

FastRestful fieldPayPal value
Client IDApp Client ID
Client secretApp Secret
Webhook IDThe webhook ID you copied in the previous step
ModeTest for sandbox, Live for production

Open Admin → Payment gateways and edit the PayPal row. On that form, Public key is the Client ID, Secret key is the Client secret, and Webhook secret is the Webhook ID.

If you prefer environment variables:

backend/.env
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_WEBHOOK_ID=...
PAYPAL_ENV=sandbox          # use live in production
# PAYPAL_API_BASE=          # only for a proxy or custom host

Restart the backend and workers after changing environment values. If you use the admin record, save and enable it; no restart is needed.

4. PayPal Billing plans for platform subscriptions

Skip this unless you sell platform subscriptions through PayPal. Vendor API plans use one-time Checkout orders and do not need a PayPal Billing plan.

  1. Create a Product in PayPal.

    In the PayPal dashboard, open the Billing/Subscriptions area and create a product that represents your platform.

  2. Create a monthly and/or annual Plan.

    Set the exact amount, currency, and interval that match the plan in FastRestful. Create a separate plan for each interval.

  3. Copy each Plan ID.

    PayPal plan IDs start with P-….

  4. Paste them into the platform plan.

    Open Admin → Platform plans and fill PayPal monthly plan ID and PayPal annual plan ID.

  5. Confirm the webhook includes subscription events.

    The subscription is created in PENDING state. It becomes active only after BILLING.SUBSCRIPTION.ACTIVATED arrives.

5. PayPal Payouts for vendors

  1. Confirm the PayPal business account is approved for Payouts.
  2. Store the vendor's PayPal receiver email as their payout destination with PATCH /api/v1/orgs/{id}/payout-provider/.
  3. Optionally set PAYOUT_PROVIDER=paypal, or PAYOUT_PROVIDER=stripe,paypal to allow both.
  4. Keep the PAYMENT.PAYOUTS-ITEM.* events selected on the webhook.

6. Test PayPal sandbox

  1. Use sandbox credentials.

    Set PAYPAL_ENV=sandbox, use the sandbox app's Client ID and secret, and the sandbox webhook ID.

  2. Create a sandbox buyer account.

    The PayPal Developer Dashboard provides test personal accounts. Sign in to sandbox.paypal.com with that buyer when checkout opens.

  3. Complete a test payment.

    Approve the order and wait for the redirect back to FastRestful.

  4. Check the webhook receipt.

    Open Admin → Payment events. You should see CHECKOUT.ORDER.APPROVED, then PAYMENT.CAPTURE.COMPLETED, and the invoice should become paid.

  5. Resend a delivery if needed.

    In the PayPal dashboard, open the webhook's event deliveries and resend the failed event. FastRestful deduplicates by event ID.

PayPal webhook troubleshooting

FastRestful responseMeaningFix
400 PayPal webhook is not configured.The client ID or client secret is missing, or the admin record is disabled.Fill the client ID and secret and enable the record. Restart if you use environment variables.
400 Bad signature.The webhook ID is missing, belongs to another app/environment, or the signature headers were stripped.Copy the webhook ID from the same sandbox/live app. Do not use the URL or client secret. Check that your proxy forwards Paypal-* headers.
404 Unknown invoice for PayPal capture.The capture arrived before the local invoice reference existed or the event came from another installation.Resend the event from PayPal after confirming the checkout was created by this installation.
{"status":"ignored"}The event is not one FastRestful handles, or the subscription/order reference is unknown.Check that the event types above are selected. Confirm the checkout was created by this FastRestful environment.
{"status":"duplicate"}The same event ID was delivered twice.No action. This is the idempotency guard working.

Flutterwave, step by step

  1. Create and verify a Flutterwave account.

    Complete the business profile and KYC before using live mode.

  2. Copy the API keys.

    Open Settings → API Keys. Copy the public key and secret key. Test and live keys are separate.

  3. Add the webhook URL.

    Open Settings → Webhooks and set:

    Flutterwave webhook URL
    https://api.example.com/api/v1/billing/flutterwave-webhook/
  4. Create a secret hash.

    Enter a long random string as the webhook secret hash. Put the same value in FLUTTERWAVE_WEBHOOK_SECRET or the admin record. This is a value you choose, not a key Flutterwave generates.

  5. Enter the keys in FastRestful.

    Edit the Flutterwave row in Admin → Payment gateways, or set FLUTTERWAVE_PUBLIC_KEY, FLUTTERWAVE_SECRET_KEY, and FLUTTERWAVE_WEBHOOK_SECRET.

  6. Test a checkout.

    FastRestful handles charge.completed with status successful. Other events are acknowledged and ignored.

Paystack, step by step

  1. Create and activate a Paystack account.

    Complete the business activation steps before using live keys.

  2. Copy the API keys.

    Open Settings → API Keys & Webhooks. Copy the public and secret keys for test or live mode.

  3. Add the webhook URL.
    Paystack webhook URL
    https://api.example.com/api/v1/billing/paystack-webhook/
  4. Leave the webhook secret blank.

    Paystack signs each webhook with your secret key using HMAC-SHA512. FastRestful falls back to PAYSTACK_SECRET_KEY when PAYSTACK_WEBHOOK_SECRET is empty. Only set an override if Paystack support gives you a separate signing key.

  5. Enter the keys in FastRestful.

    Edit the Paystack row in Admin → Payment gateways, or set PAYSTACK_PUBLIC_KEY and PAYSTACK_SECRET_KEY.

  6. Test a checkout.

    FastRestful handles charge.success and compares the amount in the smallest currency unit (for example kobo for NGN).

Razorpay, step by step

  1. Create and verify a Razorpay account.

    Complete KYC and activate live payments.

  2. Generate API keys.

    Open Account & Settings → API Keys and generate a key. Copy the Key ID and Key Secret.

  3. Add the webhook.

    Open Account & Settings → Webhooks → Add New Webhook and set:

    Razorpay webhook URL
    https://api.example.com/api/v1/billing/razorpay-webhook/

    Select payment_link.paid. FastRestful creates a Razorpay Payment Link for checkout and reconciles that event. Set a strong webhook secret; Razorpay signs with HMAC-SHA256, and the same value must be stored in FastRestful.

  4. Enter the keys in FastRestful.

    Edit the Razorpay row in Admin → Payment gateways, or set RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, and RAZORPAY_WEBHOOK_SECRET.

  5. Test a checkout.

    Open the Payment Link, pay with a test method, and confirm the invoice is paid and the event is recorded once.

The 10-minute acceptance test

Run this for every provider before going live:

  • Provider shows as configured and enabled in Admin → Payment gateways.
  • A test checkout opens the provider's page and returns to the correct frontend URL.
  • The signed webhook arrives and appears once in Admin → Payment events.
  • The invoice becomes paid only after the webhook, not on the redirect.
  • The subscription becomes active and a consumer key works.
  • A refund returns to the original payment method and updates the ledger.
  • If payouts are enabled, a test transfer reaches the vendor destination and reconciles.
  • A duplicate webhook delivery is acknowledged without double-settling.

Common webhook mistakes

MistakeWhat happensFix
Using http://localhost as the webhook URLThe provider cannot reach your machine.Use a public HTTPS URL or a tunnel for local testing.
Mixing test and live credentialsCheckout opens, but webhook verification fails or the wrong account is charged.Keep the entire set in the same mode: keys, webhook ID/secret, and provider environment.
Pasting the PayPal webhook URL as PAYPAL_WEBHOOK_IDEvery PayPal event returns Bad signature.Copy the webhook ID from the app's Webhooks detail page.
Using the Stripe secret key as the webhook secretStripe deliveries fail verification.Use the endpoint signing secret (whsec_…).
Forgetting to subscribe to an eventThe provider never sends it, so the invoice stays open.Select the event list for that provider exactly as shown above.
Changing the tunnel URLDeliveries go to an old host and fail.Update the provider dashboard and FastRestful together.
Wrong amount or currencyFastRestful rejects settlement to protect the ledger.Make the plan and provider price match exactly.
Proxy strips signature headersVerification fails even with correct secrets.Forward the provider headers unchanged through Nginx, Cloudflare, or your load balancer.

Go-live checklist

  1. Switch to live credentials.

    Replace test keys, webhook IDs, and secrets with live values. Set PAYPAL_ENV=live for PayPal.

  2. Register the production webhook URLs.

    Use your real API hostname, not a tunnel, and confirm HTTPS works from the public internet.

  3. Set the frontend publishable key.

    Use the live Stripe publishable key and rebuild the frontend.

  4. Match prices and plan IDs.

    Confirm Stripe Price IDs and PayPal Billing plan IDs point to live objects with the exact amount, currency, and interval.

  5. Take one small real payment and refund it.

    Verify the webhook, invoice, entitlement, refund, and ledger entries. This is the only way to prove the live path.

  6. Watch the first day of events.

    Check Admin → Payment events for failures and confirm the provider dashboard shows successful deliveries.

Experience8 min read

Media library & CDN

One storage pipeline for media assets, product logos, vendor photos, user avatars, and CMS images. Local filesystem in development; S3 + CloudFront in production.

Supported types and limits

KindExtensionsLimit
Imagejpg, jpeg, png, webp, gif8 MB
Videomp4, webm64 MB
Audiomp3, wav, ogg16 MB
Documentpdf, txt, csv16 MB
Archivezip32 MB

Validation checks extension, declared content type, magic-byte signature, and size. Identical bytes inside one workspace are deduplicated by SHA-256 checksum. GET /api/v1/media/supported-types/ drives the UI accept hints.

Storage modes

MEDIA_STORAGEBehavior
localDjango filesystem storage under MEDIA_ROOT; served at /media/.
s3S3 via django-storages. UUID object names are immutable and written with a one-year immutable cache header.

S3 mode requires boto3 and django-storages in the production environment. manage.py check fails if the bucket is missing and warns when neither a CDN domain nor a public-read ACL is configured.

CloudFront setup

  1. Create a private bucket with Block all public access enabled.
  2. Create a CloudFront distribution with the bucket origin and Origin Access Control (OAC).
  3. Grant the distribution s3:GetObject on arn:aws:s3:::<bucket>/* via the generated bucket policy.
  4. Point MEDIA_CDN_DOMAIN at the distribution or a CNAME with an ACM certificate.
  5. Optionally set MEDIA_CDN_DISTRIBUTION_ID to enable purge actions.
Because object names are immutable, replaced uploads get a new key. Long-lived CDN caching is safe and purges are rarely needed. Set MEDIA_S3_ACL=public-read only when serving directly from S3 without CloudFront.

Library workflows

  • Console: staff upload and manage the library; tenants see a read-only workspace shelf at /dashboard/media or /vendor/media.
  • Collections: workspace-scoped folders/categories with search, filters, and bulk moves.
  • Rename presets: kebab, snake, Title Case, UPPER, or keep, with prefixes, suffixes, and padded sequences.
  • Focal points: per-asset focal_x/focal_y (0–100) used by CMS image blocks.
  • Range selection: shift-click a range, download, copy URL, and open the previous/next asset in the drawer.
  • Usage badges: see where an asset is referenced before deleting it.

Picker semantics

Picking a library asset copies its bytes into the target field. Replacing or deleting the library asset never breaks a picked image, and each model keeps its own storage lifecycle. Cross-workspace assets are rejected.

Wired targets: product logo, vendor profile/cover, user avatar, admin branding, category images, CMS images, and content block media.

Media API

EndpointPurpose
GET/POST /api/v1/media/List with kind/search/ordering and upload multipart.
PATCH/DELETE /api/v1/media/{id}/Metadata, alt text, tags, and deletion.
POST /api/v1/media/bulk-delete/Batch delete by ids.
GET /api/v1/media/supported-types/Type registry and limits.
GET /api/v1/media/storage-info/Staff-only backend, bucket, CDN, ACL, and sample URL diagnostics.

Environment

See Configuration for the media and spec storage matrix, including endpoint URL, CDN domain, distribution ID, and ACL behavior.

Experience12 min read

Newsletter studio

Staff-only email marketing with audiences, block-based templates, campaigns, A/B subject tests, segments, automations, per-link analytics, and transactional system mail.

FastRestful newsletter studio with growth, deliverability, campaigns, subscribers, and email activity
The newsletter studio is a separate operational surface inside the control plane, not a buried settings tab.

Quick start

  1. Open /admin/newsletter/ → Settings and configure the provider, sender, public backend URL, postal address, and unsubscribe copy.
  2. Lists & contacts → create a list; add or import contacts as email, name, tags.
  3. Templates → duplicate a built-in starter or start blank, design with the block editor, then save and preview.
  4. Campaigns → choose a template, lists, optional segment, subject(s), and send a test to yourself.
  5. Automations → add email and wait steps; list joins or tags trigger enrollment.
  6. System emails → bind templates to transactional triggers.
Provider setup: for the exact dashboard steps for SES, Mailgun, SendGrid, Postmark, Brevo, Mailjet, SparkPost, Resend, or custom SMTP, see External integrations → Email.
scheduled work
python3.11 manage.py newsletter_beat
# or schedule the Celery tasks:
# newsletter.send_due_campaigns
# newsletter.process_automations

Email providers

ProviderCredential notes
SMTP (custom)Host, port, username, password, TLS/SSL; blanks inherit EMAIL_*.
Amazon SESSMTP credentials and region.
MailgunAPI key and sending domain; EU region supported.
SendGridBearer API key.
PostmarkServer token.
BrevoAPI key.
MailjetAPI key and secret.
SparkPostBearer API key.
ResendBearer API key.

Secrets are encrypted at rest. A test button sends through the selected provider before you commit a campaign.

Templates and designer

  • Blocks: header, hero, text, image, button, divider, spacer, image + text, features, quote, social links, raw HTML, footer.
  • Advanced blocks: columns, testimonial, table, video, product card, CTA banner, footer note.
  • Variants for hero layouts, text size/alignment, image frames/captions, button style/size/alignment, divider style/width, theme radius, and content width.
  • Drag-and-drop plus keyboard reorder, debounced live preview, plain-text alternative, preheader, and sanitized URLs.
  • Placeholders: {{first_name}}, {{action_url}}, {{message}}, {{site_name}}, {{unsubscribe_url}}, {{preferences_url}}.
  • 18 built-in templates across launches, digests, webinars, case studies, win-backs, NPS, promos, changelogs, editorials, community spotlights, onboarding, and service announcements.

Campaigns

  • Audience is selected lists (union) ∩ optional segment, deduplicated; subscribed contacts only, with bounced/complained addresses excluded.
  • A/B subject tests split a percentage of recipients, wait for a window, then send the winner to the remainder. Manual “decide winner now” is available.
  • Resend to unopened creates a draft targeting only contacts with no open from the original campaign.
  • Scheduling, pause/resume/cancel, test send, and campaign calendar.
  • Reports: delivery, opens, clicks, unsubscribes, A/B comparison, per-link click table, engagement timeline, and latest recipients.
  • UTM defaults are applied to campaign links; only http(s) targets are accepted.

Segments and automations

Segments combine lists, tags (all/any), contact status, engagement windows (opened, clicked, not opened within N days), and join recency. Counts are evaluated live and reused by campaigns.

Automations trigger on list join, tag, manual, or API. Steps send an email or wait N hours. Runs advance idempotently and record failures. Enrollment is deduplicated per contact.

System emails

If an active studio template is bound to a trigger, it renders HTML + text; otherwise the legacy plain message is sent. Bound keys include welcome, password reset, login code, security alert, platform notice, invoice opened, payment failed, vendor approved, quota alert, organization invite, and activity digest.

Public signup and preferences

  • POST /api/v1/newsletter/subscribe/ with optional double opt-in confirmation.
  • GET /confirm/<token>/, GET /unsubscribe/<token>/, and RFC 8058 one-click POST.
  • Preference center at /api/v1/newsletter/preferences/<token>/ to toggle lists or unsubscribe globally.
  • Open pixel and click redirects rewrite links when tracking is enabled.
  • Outgoing mail includes List-Unsubscribe and List-Unsubscribe-Post headers.

Deliverability and operations

  • Bounce, complaint, unsubscribe, and growth charts; keep bounce and complaint rates near zero.
  • Send-time heatmap by local weekday × hour, with best-window recommendations.
  • Audience estimates and sample addresses before sending.
  • Contacts page with status, tags, custom fields, memberships, suppression, deliveries, events, and automation runs.
  • CSV exports for contacts, campaign recipients, and links.
  • Run newsletter_beat every few minutes for schedules, A/B decisions, and automation delays.
Experience8 min read

Announcements & banners

Admin-designed public banners for top bars, footer placements, and in-page ads — with targeting, scheduling, dismissal rules, visual experiments, and impression/click analytics.

Placements

KeyWhere it appears
topbarFull-width bar above the public header.
footerFull-width banner above the footer grid.
footer_leftLeft footer ad slot.
footer_rightRight footer ad slot.
centerIn-page banner between the header and page content.

One banner is served per placement, chosen by priority then recency. A banner can target multiple placements.

Targeting and scheduling

  • Paths: include and exclude patterns such as /pricing, /docs/*, or *.
  • Audience: everyone, signed-out visitors, or signed-in users.
  • Locale and device: comma-separated allow-lists; tablet falls back to mobile when mobile is allowed.
  • Schedule: start/end timestamps and an active switch.
  • Priority: higher numbers win when multiple banners match.

Design and built-ins

  • Layouts: bar, card, split, minimal.
  • Badge, headline, body, CTA label/URL/style/new-tab, image, side, colors, gradients, alignment, and live preview.
  • Optional icon/emoji, decorative patterns (dots, grid, stripes, glow), and reduced-motion-safe entrance animations.
  • 32 built-in banners across launches, trust/status, promotions, events, lifecycle, editorial, community, and seasonal campaigns.
  • Custom HTML with {{click_url}}, {{dismiss_url}}, and {{impression_url}} placeholders so tracking still works.
  • Dismiss frequency: every page load, once per session, once per day, or once ever.

Delivery and tracking

public endpoints
GET  /api/v1/announcements/?path=/pricing&locale=en&placement=topbar
POST /api/v1/announcements/<id>/impression/
POST /api/v1/announcements/<id>/dismiss/
GET  /api/v1/announcements/<id>/click/

Payloads include server-rendered inline-styled HTML plus impression_url, dismiss_url, and frequency. Events record placement, path, locale, device, referrer, user agent, and IP.

Experiments and reporting

  • Banners with the same priority share traffic by weight (1–1000).
  • Set an experiment name and variant label on multiple banners; one variant is chosen per request based on weight.
  • Experiment reports show impressions, clicks, CTR, and the leading variant.
  • Reports chart 30/90-day daily activity and break down placement, top pages, devices, and recent events.
  • Admin lists show counters, activate/pause actions, and read-only event records.

Operations

Banner events are append-only. Dismissal state is stored in the visitor’s browser and counted server-side. The public script skips bot user agents; use a normal browser user agent for public-banner QA.

Experience7 min read

Short links

Campaign-ready short links, redirects, QR codes, UTM defaults, fallbacks, click analytics, and broken-destination checks — available in both the consumer dashboard and the vendor studio.

  • Create short links with a custom slug, destination, tags, redirect type, expiry, and active state.
  • 301 or 302 redirects; expired or disabled links can fall back to a safe destination or a branded 410 page.
  • UTM defaults (utm_source, utm_medium, utm_campaign) appended at redirect time; inbound campaign parameters take precedence.
  • QR SVG generation with custom color and campaign metadata.
  • Click analytics with country, device, referrer, and campaign context.
  • Broken-destination health checks with error details and one-click re-check.
  • CSV import/export and bulk selection actions.

Open Links in the consumer dashboard (/dashboard/links) or vendor studio (/vendor/links). The editor includes title, destination, slug, tags, redirect type, expiry, active switch, and staff-only site-wide scope. The table exposes copy, QR, re-check, edit, delete, health, and status actions.

VariablePurpose
SHORTLINK_BASE_URLPublic short-link origin, e.g. https://go.example.com.
NEWSLETTER_TRACKING_BASE_URLFallback origin when the short-link base is empty.
FRONTEND_URLFinal fallback origin.
schedule health checks
python3.11 manage.py checklinks --limit 200 --timeout 6
EndpointPurpose
GET/POST /api/v1/short-links/List and create workspace links.
PATCH/DELETE /api/v1/short-links/{id}/Edit or delete.
GET /api/v1/short-links/{id}/qr/QR SVG with optional color.
POST /api/v1/short-links/{id}/check/Re-check destination health.
GET /api/v1/short-links/export/CSV export.
POST /api/v1/short-links/import/CSV import.

Public redirects resolve by slug, record the click, then redirect. Admin lists include click/health columns, re-check/activate/disable actions, a “Shorten a link” quick action, and a read-only click log.

Experience12 min read

Site, CMS & locales

The Django control plane owns the public shell: identity, search/share defaults, navigation, pages, blog posts, legal documents, custom CSS/JS, maintenance mode, and locale overlays.

Operator surfaces

Admin routeOwns
/admin/platform/sitesettings/Marketplace policy, brand assets, global metadata, header/footer copy, announcement, support contact, two-factor policy, maintenance mode.
/admin/platform/sitenavigationitem/Ordered header/footer links, placement, visibility, new-tab behavior.
/admin/platform/contentpage/Pages, blog posts, legal drafts, structured sections, cover media, SEO, publishing state.
/admin/platform/siteasset/Custom CSS/JS snippets, path scoping, preview, revision history.
/admin/platform/sitelocale/Bundled locales, admin overlays, translation coverage, import/export.

Site settings

  • Logo, favicon, and social share image with media picker and post-commit cleanup.
  • Global meta title, description, and share image fallback.
  • Marketplace mode, default fee percentage, maintenance flag.
  • Header CTA label + destination (must be supplied together).
  • Announcement banner enabled/text/url.
  • Footer headline, summary, copyright, support email.
  • Built-in challenge or Google reCAPTCHA for public forms.
  • Required two-factor policy for all accounts.
  • Theme colors, fonts, and live public-shell preview.

Structured content

Pages and posts are assembled from ordered sections, not raw HTML. Available section kinds include prose, table, code, image, video, split, quote, tabs, multi-language code, endpoint sample, testimonials, feature list, FAQ, steps, metrics, card grid, divider, hero, logo strip, timeline, link list, callout, API collection, category collection, top APIs, and recent articles.

  • Prose supports paragraphs separated by a blank line; list items are one per line.
  • Actions require both a label and destination; links accept site paths, HTTPS, mailto, or tel.
  • Drafts never appear publicly; published pages appear in site content, metadata, and sitemap routes.
  • Page-level SEO overrides global defaults per field.
  • Legal pages join the default footer legal group only after publication.

Custom CSS & JS

Admin → Custom assets injects CSS or JavaScript into the public site without a deploy. Each snippet has a kind, placement (head or footer), and scope (entire site, selected paths, or one page). Paths support exact matches and /prefix/* wildcards.

  • Public payload: GET /api/v1/platform/assets/ returns active snippets plus the configured csp_nonce.
  • Preview: each snippet has an in-admin iframe preview with forced injection via ?fr_preview_asset=<id>.
  • Revisions: every save creates an immutable snapshot with author and note; any revision can be restored as a new entry.
  • CSP: set the same nonce in Site settings and your edge policy; tags receive it automatically.

Locales and translation overlays

English is the canonical source bundle. French ships complete. Every other locale is a sparse admin overlay merged over English at runtime.

  • Open Site locales → Manage translations to inspect coverage, edit bundled French, or add a locale.
  • Bundled French appears at 100% without a database row. Saving a field creates an admin overlay without changing the shipped fallback.
  • Export JSON or TypeScript, translate offline, and import back. Unknown keys are ignored and reported.
  • Public locale API returns active overlays; the frontend merges them over bundled English and French.
  • After editing lib/i18n/*.json, run npm run export:i18n and restart the API to refresh its process cache.

Maintenance mode

Maintenance pauses every frontend route and customer API/gateway call. Staff admin, health probes, identity-provider callbacks, and signed payment/payout webhooks remain available. Public availability is exposed anonymously with the maintenance flag, notice, and retry interval.

Public contracts

EndpointExposes
GET /api/v1/platform/branding/Logo, favicon, update time.
GET /api/v1/platform/site-metadata/Title, description, social image, favicon, update time.
GET /api/v1/platform/site-content/Active navigation, shell copy, published pages/posts.
GET /api/v1/platform/site-pages/Published route metadata for SEO and sitemap.
GET /api/v1/platform/availability/Maintenance flag, notice, retry interval.
GET /api/v1/platform/assets/Active custom CSS/JS and CSP nonce.
GET /api/v1/platform/locales/Active locale overlays.
Advanced14 min read

External integrations, step by step

Email, object storage, single sign-on, infrastructure, and outbound webhooks. Each section tells you what to do on the provider's website first, then exactly which values to copy into FastRestful.

Payments are in their own guide. Stripe, PayPal, Flutterwave, Paystack, Razorpay, webhooks, and payouts are covered in Payment gateways, step by step.

Which value wins?

  • Environment variables provide deployment defaults.
  • Admin-managed records override matching environment values field by field.
  • Disabling an admin record turns that provider off everywhere, even if environment keys remain set.
  • Frontend provider values are baked at build time. Rebuild after changing NEXT_PUBLIC_*.
  • Run the provider's test action before sending live traffic.

Email and newsletter providers

FastRestful can send campaign, automation, and system email through your own provider. Configure it in Admin → Newsletter → Email settings, or inherit SMTP from EMAIL_* environment variables. API keys and secrets are encrypted at rest.

The six-step setup

  1. Create an account with one provider.

    Use a transactional provider such as Amazon SES, Mailgun, SendGrid, Postmark, Brevo, Mailjet, SparkPost, Resend, or any SMTP host.

  2. Verify your sending domain or sender address.

    Most providers will not deliver until the domain or email is verified. Add the DNS records the provider gives you.

  3. Create the API key or SMTP credentials.

    Use the smallest permission that can send email. For SMTP, copy the host, port, username, and password.

  4. Enter the values in FastRestful.

    Open Admin → Newsletter → Email settings. Choose the provider, paste the credentials, and set the from name, from email, reply-to, postal address, and unsubscribe copy.

  5. Set the public backend URL.

    This is used in tracking pixels, confirmation links, and unsubscribe links. It must be the public origin that reaches Django, not the frontend dev server.

  6. Send a test email to yourself.

    Use the built-in test action. Check the full message source for the expected sender and links before sending a campaign.

Provider cheat sheet

ProviderWhat to do on the provider siteWhat to paste into FastRestful
SMTP (custom)Get the host, port, username, and password from your mail host or hosting panel.Host, port, username, password, TLS/SSL.
Amazon SESVerify the domain or sender, request production access, then create SMTP credentials in the SES console.SMTP host such as email-smtp.us-east-1.amazonaws.com, port 587, SMTP username and password. Keep TLS on.
MailgunAdd and verify the sending domain, then create an API key. Note whether the domain is in the US or EU region.API key, sending domain, optional EU region, optional API endpoint.
SendGridVerify a sender or domain, then create an API key with Mail Send permission.Bearer API key.
PostmarkCreate a server and verify a sender signature or domain.Server token.
BrevoVerify the sender or domain and create a v3 API key.API key.
MailjetVerify the sender or domain and create an API key and secret.API key and secret.
SparkPostVerify the sending domain and create an API key with send permission.Bearer API key.
ResendVerify the sending domain and create an API key.Bearer API key.

Deliverability is a DNS job. Authenticate SPF, DKIM, and DMARC before increasing volume. Start with small sends, monitor bounces and complaints, and keep the unsubscribe link working. Campaigns, automations, and system mail can use different templates but share the same provider.

Object storage and CDN

Local filesystem storage is fine for development. Production should use S3-compatible storage plus a CDN, so uploads survive deployments and assets load quickly.

1. Create the S3 bucket and permissions

  1. Create a private bucket.

    In the AWS console, create a bucket in the region you want. Keep Block all public access enabled. CloudFront will read it through OAC.

  2. Create an IAM user or role.

    The standard credential chain is supported: environment variables, an instance profile, or IRSA on Kubernetes. Do not hard-code keys in the repository.

  3. Grant only the permissions FastRestful needs.
    minimum IAM policy
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::my-media-bucket"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
          "Resource": "arn:aws:s3:::my-media-bucket/*"
        }
      ]
    }
  4. Install the storage libraries.

    Run pip install boto3 django-storages in the backend environment and add them to your deployment requirements.

  5. Set the environment values.
    backend/.env
    MEDIA_STORAGE=s3
    MEDIA_S3_BUCKET=my-media-bucket
    MEDIA_S3_REGION=us-east-1
    # MEDIA_S3_ENDPOINT_URL=   # only for S3-compatible providers
    # MEDIA_S3_ACL=            # omit when using CloudFront OAC
  6. Restart and upload a test asset.

    Open the media library and upload an image. It should appear in the bucket and load from the configured CDN or S3 URL.

2. Put CloudFront in front of the bucket

  1. Create a CloudFront distribution.

    Choose the bucket as the origin and create an Origin Access Control (OAC).

  2. Update the bucket policy.

    Allow the CloudFront distribution to read objects. The CloudFront console can generate the policy for you.

  3. Set the FastRestful values.
    backend/.env
    MEDIA_CDN_DOMAIN=cdn.example.com
    MEDIA_CDN_DISTRIBUTION_ID=E1234567890ABC
    # MEDIA_S3_ACL=public-read   # only if serving S3 directly, not recommended
  4. Allow cache invalidation.

    Add cloudfront:CreateInvalidation to the IAM policy if you want the media library's purge button to work.

  5. Test with a fresh upload.

    Upload a new file, open it through the CDN domain, then replace it and use the purge action.

3. S3-compatible providers

Cloudflare R2, MinIO, DigitalOcean Spaces, and similar services work through the S3 API. Set MEDIA_S3_ENDPOINT_URL to the provider's S3 endpoint and keep the bucket private if the provider supports a CDN or signed URLs. Verify upload, download, and delete before relying on it in production.

4. OpenAPI spec storage

By default, imported OpenAPI contracts are stored in the database. To store them in S3, set:

backend/.env
SPEC_STORAGE=s3
SPEC_S3_BUCKET=my-spec-bucket
SPEC_S3_PREFIX=specs/
SPEC_S3_REGION=us-east-1
# SPEC_S3_ENDPOINT_URL=

See Media library & CDN for upload limits, supported file types, and CDN behavior.

Single sign-on

Both OIDC and SAML preserve safe relative destinations and organization invite tokens. Password login can remain enabled as a fallback.

OIDC, step by step

  1. Create an OIDC application in your identity provider.

    Use Okta, Auth0, Microsoft Entra ID, Google Workspace, Keycloak, Authentik, or any compliant OIDC provider.

  2. Register the callback URL.
    redirect URI
    https://api.example.com/api/v1/auth/sso/callback/

    Use the exact hostname users reach. A trailing slash mismatch will fail the callback.

  3. Allow the authorization-code flow and scopes.

    Request openid, email, and profile.

  4. Copy the issuer, client ID, and client secret.
    backend/.env
    SSO_ISSUER=https://login.example.com/realms/acme
    SSO_CLIENT_ID=fastrestful
    SSO_CLIENT_SECRET=...
  5. Restart the backend.

    The SSO button is hidden until the provider is configured.

  6. Test with a real identity.

    Sign in through the button and confirm the callback returns to the expected frontend route.

SAML 2.0, step by step

  1. Install the optional dependency.

    Run pip install lxml before enabling SAML.

  2. Copy the SP metadata.

    Give your identity provider this URL, or upload the XML it returns:

    SP metadata
    https://api.example.com/api/v1/auth/saml/metadata/
  3. Set the ACS URL and entity ID in the IdP.

    The assertion consumer service is:

    ACS URL
    https://api.example.com/api/v1/auth/saml/callback/

    The default entity ID is fastrestful. You can change it with SSO_SAML_ENTITY_ID.

  4. Copy the IdP SSO URL and signing certificate.
    backend/.env
    SSO_SAML_ENABLED=1
    SSO_SAML_ENTITY_ID=fastrestful
    SSO_SAML_SSO_URL=https://idp.example.com/sso
    SSO_SAML_IDP_CERT=-----BEGIN CERTIFICATE-----...
    # SSO_SAML_AUDIENCE=fastrestful
  5. Restart and test.

    Start a login from the frontend and confirm the IdP assertion returns to the expected page.

Infrastructure providers

ServiceWhat to doFastRestful setting
PostgreSQLProvision a managed database or your own server. Enable automated backups and monitor connection limits.DATABASE_URL. Required in production.
RedisProvision a managed Redis instance. Enable persistence if you rely on Celery retries.REDIS_URL. Required in production.
CeleryRun a worker and beat process under systemd, Supervisor, or your platform's process manager.CELERY_BROKER_URL (defaults to REDIS_URL).
NginxTerminate TLS, proxy to Gunicorn, serve /static/ and /media/, and forward host and client-IP headers.See Deployment → Nginx.
Cloudflare / CDNProxy DNS, enable TLS, and raise upload limits to match the media settings. Keep webhook paths out of aggressive caching.DNS and cache rules.
Run the scheduler. Celery beat drives monthly settlement, moderation escalation, email, and availability checks. Without a broker, run the settlement commands manually. See Operations.

API tooling and outbound webhooks

OpenAPI, Swagger, and Postman

  • OpenAPI import: import OpenAPI 3.x or Swagger 2.0 into a draft version. The release pipeline reconciles endpoints and flags breaking changes.
  • Platform schema: export it at /api/schema/ with python3.11 manage.py spectacular --file schema.yml --validate.
  • Swagger UI: served at /api/docs/ by the backend.
  • Postman / starter kits: product endpoints expose downloadable bundles for consumers.

Outbound webhooks, step by step

  1. Create the endpoint in FastRestful.

    Open the control plane's webhook endpoints section and add your receiver URL. It must be HTTPS.

  2. Choose events and a signing secret.

    Store a strong secret. FastRestful signs the raw request body with HMAC-SHA256.

  3. Send a test delivery.

    Confirm your receiver returns a 2xx response.

  4. Verify the signature on your server.

    Compare your own HMAC-SHA256 of the raw body with the X-FastRestful-Signature header. The value is sha256=<hex>. The event name is in X-FastRestful-Event.

  5. Watch the delivery log.

    Retries are safe and the delivery history is read-only evidence. Handle duplicate event names idempotently.

Payment webhooks are separate. Provider webhooks settle money and are registered in each provider's dashboard. Vendor outbound webhooks are for your own integrations. Do not point a payment provider at a vendor webhook endpoint.

Integration acceptance checklist

  • Provider appears as configured and enabled in the admin or settings page.
  • A real test message or upload reaches the provider and the returned URL loads.
  • Test email arrives; SPF, DKIM, and DMARC pass.
  • Uploaded media is stored in the bucket and served through the CDN.
  • The OIDC or SAML callback returns to the expected frontend route.
  • Outbound webhook signatures verify on the receiver.
  • Retries do not duplicate side effects.
  • Test and live credentials are not mixed.
Advanced9 min read

Backups & upgrades

A repeatable backup, restore, and upgrade path for a self-hosted FastRestful deployment. Backups come first; the upgrade method depends on how you bought and deployed the product.

Licensed install from Dakidarts? Use Licensing & official updates for signed official releases and one-click rollback. This guide still owns the backup and restore plan, and the manual source-upgrade path.

What to back up

DataWhyMethod
PostgreSQLTenants, products, releases, subscriptions, invoices, audit, content, keys metadata.Nightly logical dump plus provider continuous backup.
S3 mediaLibrary assets, logos, avatars, CMS images.Versioning and cross-region replication where required.
Spec blobsOpenAPI contracts when SPEC_STORAGE=s3.Included in S3 backup.
Environment secretsDJANGO_SECRET_KEY, FIELD_ENCRYPTION_KEY, provider credentials.Secret manager with access control and rotation history.
Frontend build configNEXT_PUBLIC_* values and hosting settings.Infrastructure-as-code or deployment notes.
Encryption key warning. Losing FIELD_ENCRYPTION_KEY makes encrypted API keys and provider secrets unrecoverable. Back it up separately from the database, with strict access control.

PostgreSQL backup

shell
pg_dump --format=custom "$DATABASE_URL" > fastrestful-$(date +%F-%H%M).dump
pg_dump --format=plain "$DATABASE_URL" | gzip > fastrestful-$(date +%F-%H%M).sql.gz

# Verify the dump is readable
pg_restore --list fastrestful-$(date +%F-%H%M).dump > /dev/null

Restore into a clean database

shell
createdb fastrestful_restore
pg_restore --clean --if-exists --no-owner \
  --dbname=fastrestful_restore fastrestful-2026-10-21.dump

# Point a staging deployment at the restored database and run:
python3.11 manage.py migrate --check
python3.11 manage.py check

Test restores quarterly. A backup that has never been restored is an assumption, not a recovery plan.

Two ways to upgrade

PathUse it whenStart here
Official update centerYou bought FastRestful from Dakidarts and run a source or VM deployment.Licensing & official updates
Manual source upgradeYou maintain a custom fork, build your own images, or do not use LicenseHub.The sequence below.

Manual upgrade sequence

Use this only for a custom deployment that is not managed by the official update center.

  1. Read the release notes and check for migration or environment changes.
  2. Back up PostgreSQL, media, and secrets.
  3. Put the platform in maintenance mode if a migration requires a write pause.
  4. Pull code, install backend and frontend dependencies.
  5. Run migrations, collect static files, and build the frontend.
  6. Restart API and workers; verify readiness and a smoke request.
  7. Exit maintenance mode and monitor error rate, queue depth, and webhook receipts.
After either upgrade path, restart Gunicorn, the Celery worker and beat, and the frontend build process if it runs on the same host. Then check /api/health/ready, send one smoke request, and open Admin → Payment events to confirm no provider deliveries failed during the restart.
shell
git pull --ff-only
pip install -r backend/requirements.txt
npm ci

python3.11 backend/manage.py migrate --noinput
python3.11 backend/manage.py collectstatic --noinput
npm run build

sudo systemctl restart fastrestful
sudo systemctl restart fastrestful-worker fastrestful-beat
curl -f https://api.example.com/api/health/ready

Rollback

  • Code rollback is straightforward only when the previous release did not require an irreversible database migration.
  • Keep a database snapshot before every migration. Restore the snapshot and the matching code version together.
  • Never edit an already-applied migration in production; add a new corrective migration.
  • After rollback, re-run the smoke request and inspect queued tasks for schema assumptions.

Migration safety

preflight
python3.11 manage.py showmigrations
python3.11 manage.py makemigrations --check --dry-run
python3.11 manage.py migrate --plan
  • Additive changes first, backfill second, remove old fields in a later release.
  • Avoid long table locks on high-volume metering, request-log, and audit tables.
  • Run maintenance mode for migrations that change money-state or release-state columns.
  • Keep Celery paused until migrations complete.

Frontend releases

The frontend build is static and self-contained. Keep the API origin and public site URL aligned with the deployment. If you deploy a new build that expects new API fields, release the backend first or keep the contract backward-compatible.

Advanced9 min read

Observability

Health, logs, metrics, and queues for a platform where requests, money, and moderation all leave evidence.

Health and readiness

ProbeWhat it answers
GET /api/health/liveIs the process alive?
GET /api/health/readyCan the process reach the database and shared cache?
GET /api/schema/Is the API contract servable?
Frontend route renderCan the public shell load branding and content?

Use readiness for load-balancer membership. A cache outage returns 503 with the failing dependency instead of serving with local-only state.

Request context

  • Every API response carries request metadata in the application logs. Preserve the request ID through your reverse proxy.
  • Gateway request logs include subscription, product, endpoint, status, latency, and redacted previews.
  • Provider events store event IDs and payload hashes, not raw secrets.
  • Audit events link actor, action, organization, target, and metadata.

Signals to monitor

AreaSignalAction threshold
API5xx rate, p95 latencyPage on sustained 5xx or latency regression.
GatewayUpstream error rate, circuit-open countInvestigate vendor health before raising quotas.
DatabaseConnections, slow queries, diskScale or add indexes before saturation.
RedisMemory, evictions, connection errorsEvictions can fail quota/rate-limit behavior.
CeleryQueue depth, task failures, beat heartbeatAlert if settlement or email queue stalls.
MoneyFailed webhooks, pending refunds, payout exceptionsReconcile before retrying blindly.
EmailBounce rate, complaints, unsubscribe rateSuppress and investigate above normal.

Logging rules

  • Never log raw API keys, provider secrets, or full payment payloads.
  • Keep request/response previews bounded and redacted.
  • Log provider event IDs and settlement outcomes, not card data.
  • Use structured fields where possible: request ID, organization, product, endpoint, status.
  • Separate audit evidence from ordinary application logs.

Queues and scheduled work

shell
celery -A config inspect ping
celery -A config inspect active
celery -A config inspect reserved
celery -A config inspect scheduled

Monitor the default and billing queues independently. Settlement runs monthly; moderation and notification work runs every 15 minutes; availability checks run daily.

Minimum alert set

  • Readiness failing for more than 2 minutes.
  • API 5xx rate above the service objective.
  • Gateway circuit open or upstream error rate spiking.
  • Webhook failures or unprocessed provider events.
  • Celery beat heartbeat missing; queue depth growing.
  • Database disk and connection saturation.
  • Backup failure or last successful backup too old.
  • Two-factor enrollment unexpectedly dropping for staff.

Diagnostic commands

shell
python3.11 manage.py check --deploy
python3.11 manage.py showmigrations
python3.11 manage.py prune_telemetry --request-log-days 90 --usage-event-days 365
python3.11 manage.py check_api_availability
python3.11 manage.py checklinks --limit 200 --timeout 6
Advanced8 min read

Scaling

Where FastRestful scales cleanly, where it needs infrastructure, and which state must stay shared.

Scale the tiers separately

topology
edge / CDN
  ├── frontend static assets
  └── API reverse proxy
        ├── Django / Gunicorn replicas
        ├── Celery workers (default queue)
        ├── Celery workers (billing queue)
        ├── Celery beat (one instance)
        ├── PostgreSQL primary + read capacity
        └── Redis (shared cache + broker)

Web tier

  • Run multiple stateless Gunicorn/Django instances behind a load balancer.
  • Sessions live in the database; cache and throttles use Redis. Do not rely on local memory in production.
  • Keep workers sized for the slowest gateway request (default timeout 15 seconds).
  • Terminate TLS at the edge and preserve the original host for product routing.

Database

  • Use PostgreSQL with connection pooling as worker counts grow.
  • High-volume tables: usage events, request logs, audit events, webhook deliveries.
  • Prune telemetry on a schedule and archive audit evidence according to policy.
  • Keep money-state writes transactional; do not shard invoice or ledger tables without a migration plan.

Redis

  • Shared cache is required for gateway rate limits and idempotency.
  • A Redis outage returns a retryable 503 before quota reservation; the gateway does not fall back to per-process state.
  • Monitor evictions. Losing idempotency records can permit duplicate upstream calls after TTL changes.
  • Use a separate Redis database or instance for the Celery broker if cache pressure grows.

Gateway capacity

  • Quota and rate limits are enforced per subscription and endpoint, not per process.
  • Upstream latency dominates request time; keep timeouts and retries conservative.
  • Idempotency storage grows with request volume; size Redis for the configured TTL.
  • Use product host routing with wildcard TLS to avoid a central path bottleneck at the edge.

Workers and scheduled work

  • Run one beat instance; multiple beat schedulers can duplicate timed work.
  • Scale billing and default queues separately.
  • Keep settlement idempotent; reruns are safe by design.
  • Backpressure email and moderation queues with worker concurrency rather than disabling retries.

Media and CDN

  • Move uploads to S3 before local disk lifecycle becomes a problem.
  • Serve through CloudFront with immutable object names and long-lived caching.
  • Keep origin access private; avoid public bucket policies.
  • Use per-asset purge only when an object key is intentionally reused.

Capacity checklist

  • Readiness and 5xx alerts are wired to paging.
  • PostgreSQL has headroom for connections, disk, and IOPS.
  • Redis has headroom for cache plus idempotency records.
  • Celery queue depth and beat heartbeat are monitored.
  • Object storage and CDN are off the web server.
  • Telemetry retention is configured before growth, not after.
Advanced10 min read

Data model & permissions

The entities that define FastRestful, the tenancy boundaries around them, and the invariants that keep money and releases consistent.

Tenancy

Organization is the tenant boundary. A user joins through a Membership with a role. The active organization travels explicitly and is resolved server-side before object access.

RoleIntent
OwnerFull workspace control, billing, team, and vendor operations. Last owner protected.
AdminTeam, applications, keys, subscriptions, publishing, billing, support.
DeveloperApplications, keys, endpoint testing, publishing where allowed.
BillingInvoices, coupons, refunds, payouts, payment methods.
ViewerRead-only workspace visibility.

Core entities

DomainEntities
IdentityUser, Organization, Membership, OrganizationInvite, VendorProfile, TwoFactorDevice, LoginCode
CatalogCategory, ApiProduct, ApiVersion, Endpoint, ApiAvailabilityCheck
AccessApplication, ApiKey, Plan, Subscription, Coupon
MeteringUsageEvent, MonthlyCounter
MoneyInvoice, Payout, LedgerEntry, PaymentEvent, PayoutProviderEvent, RefundRequest
PlatformSiteSettings, PlatformPlan, PlatformSubscription, SiteNavigationItem, ContentPage, SiteLocale, SiteAsset
EngagementEmailList, Contact, Campaign, EmailTemplate, Automation, Banner, ShortLink
TrustAbuseReport, ModerationAppeal, ModerationPolicy, SafetyEnforcement, ProfileBlock, DirectMessage
OperationsRequestLog, WebhookEndpoint, WebhookDelivery, Ticket, TicketMessage, AuditEvent, ExportJob

Invariants

  • A product has one live version at a time.
  • A live version's contract and endpoint catalog cannot be edited.
  • A subscription has immutable commercial terms; changes create a new plan.
  • An API key belongs to exactly one application and cannot cross applications.
  • A provider event ID is recorded once; replays do not settle twice.
  • Payouts follow a monotonic state machine.
  • Ledger entries are append-only.
  • The last active workspace owner cannot be removed or demoted.

Permission checks

OperationRequired authority
Team and workspace changesOwner or admin
Product publish/promoteOwner, admin, or developer in the owning org
Invoice, refund, payout, couponOwner, admin, or billing
Platform settingsSuperuser / explicit staff permissions
Vendor profile reviewStaff with vendor model permission
Moderation decisionStaff with safety model permission

Deletion and retention

  • Tickets are never deleted; they remain support evidence.
  • Live release history remains available for audit even after deprecation.
  • Audit events and provider receipts are append-only.
  • Request logs and usage events are pruned by configured retention.
  • Media deletes use the asset lifecycle and should respect in-use references.

Extending the model

  1. Start from a real workflow, not a hypothetical field.
  2. Keep platform-only data out of anonymous serializers.
  3. Validate at the model/API layer; add database constraints for money and state transitions.
  4. Add contract tests for permissions, tenant boundaries, and idempotency.
  5. Document the operator surface before shipping the model.
Advanced8 min read

Extensions

Extend the public experience and operational workflow without forking the core: custom assets, CMS sections, locale overlays, webhooks, and admin patterns.

Custom CSS and JavaScript

Admin → Custom assets injects a snippet into the public site. Each snippet has a kind, placement, scope, and active state.

FieldOptions
KindCSS or JavaScript
PlacementHeader or footer
ScopeEntire site, selected paths, or one page
Path patternsExact paths and /prefix/* wildcards
  • Every save creates an immutable revision with author and note; any revision can be restored as a new entry.
  • Preview the live site in an admin iframe with the snippet force-injected.
  • Use the configured CSP nonce in both Site settings and edge policy; injected tags receive it automatically.
  • Treat snippets as trusted operator code. They can restyle or instrument the public site.

Structured CMS sections

Pages and posts are assembled from ordered sections. The editor includes prose, table, code, image, video, split, quote, tabs, code groups, endpoint samples, testimonials, features, FAQ, steps, metrics, cards, dividers, hero, logo strip, timeline, link list, callouts, API collections, category collections, top APIs, and recent articles.

  • Use structured sections rather than pasting arbitrary HTML.
  • Set action label and destination together.
  • Use site paths, HTTPS, mailto, or tel destinations.
  • Keep meaningful image alt text; body copy can serve as a caption.
  • Published pages join navigation and sitemap metadata only when live.

Locale overlays

English is the canonical bundle; French ships complete. Add another locale in the admin, import a JSON or TypeScript bundle, and override only the keys you need.

  • Bundled values remain the fallback when an overlay key is blank.
  • Unknown keys are ignored and reported during import.
  • Export JSON or TypeScript for offline translation.
  • Run npm run export:i18n after editing the frontend bundle and restart the API.

Outbound webhooks

Vendor webhooks deliver signed events to HTTPS endpoints with retry-safe delivery history. Keep the signing secret in the vendor workspace and verify the signature before acting on a payload.

  • Use a stable endpoint URL and return a 2xx quickly.
  • Treat events as at-least-once and make handlers idempotent.
  • Use the delivery log to inspect response status and previews.
  • Do not reuse payment-provider webhook routes for vendor events.

Admin extension pattern

  1. Register the model with explicit list, filter, search, and permission behavior.
  2. Add a focused admin form rather than exposing raw internal fields.
  3. Use audit events for state-changing actions.
  4. Add a preview or report page only when it answers a real operator question.
  5. Test permission boundaries and the public/private serializer split.

API extension pattern

  • Keep tenant scoping before every query.
  • Use explicit request/response serializers.
  • Return stable error codes for client behavior.
  • Make retries safe with idempotency where money or state is involved.
  • Add OpenAPI annotations so the schema stays usable.
Reference10 min read

Administration

The staff control plane is a permission-aware Django admin surface for platform operations. It does not use demo metrics or bypass model permissions.

FastRestful staff control plane operations overview with traffic, catalog, revenue, and decision queues
The staff overview is a live, permission-aware snapshot of the platform's current records.

Access boundary

  • Staff access requires is_staff. A workspace admin role is not platform staff.
  • Two-factor can be required for every account from Site settings.
  • Django model permissions and registered ModelAdmin behavior are honored.
  • Admin reads may be broader than writes; object ownership and provider state still gate consequential actions.
  • Every sensitive action should create an audit event with actor, target, and metadata.

Operations overview

/admin/ shows a live, read-only snapshot built from current records: traffic, public APIs, active consumers, active workspaces, contact messages, two-factor adoption, platform notices, attention queues, payment state, audit stream, site experience, and locale coverage.

  • Quick actions: create API, review vendors, site settings, platform plans, content studio, newsletter, banners, links.
  • Decision queues: vendors, moderation, appeals, safety restrictions, support, failed webhooks, payouts, invoices, contact inbox.
  • Payment panel: revenue by provider, active subscriptions, MRR, pending checkouts, webhook receipts, refunds, gateway links.

Control plane sections

SectionModels / workflows
API catalogProducts, categories, versions, endpoints, reviews, availability checks, portal settings.
VendorsVendor profile review, approval, suspension, resubmission.
Finance and payoutsPayment gateways, invoices, payouts, ledger, coupons, payment events, refunds, provider events.
Support ticketsQueues, priority, status, request context, replies.
Trust & safetyAbuse reports, appeals, moderation policy, profile blocks, enforcement ledger.
Reliability and usageRequest logs, idempotency records, usage events, monthly counters, audit events.
Workspaces and accessOrganizations, memberships, invites, applications, API keys, plans, subscriptions, platform plans.
IntegrationsWebhook endpoints and delivery history.
Newsletter & emailTemplates, lists, contacts, segments, campaigns, automations, tracked mail, events, settings.
Site experienceMedia, collections, site settings, navigation, content pages, custom assets, locales.
Identity & securityUsers, login codes, groups, blacklisted/outstanding tokens.
Messaging & communityPlatform notifications, contact messages, direct messages, preferences, follows, user notifications, ticket messages.

Moderation and safety

  • Reports carry target type/label, category, evidence, priority, SLA deadline, assignment, and private/public resolution summaries.
  • Staff configure priority SLA windows, escalation thresholds, and appeal windows in Moderation policy.
  • Eligible closed reports can be appealed once inside the window; the original public decision stays attached.
  • Accepted appeals reopen the case with a fresh policy-derived SLA.
  • Resolving an upheld report can issue an auditable safety enforcement: warning, community restriction, publishing restriction, or platform restriction.
  • Enforcement records show points, public reason, effective dates, status, and only the affected user or active vendor members can read them.

Support and team

  • Tickets link consumer, product, subscription, and optional request-log context.
  • Messages advance the ticket activity timestamp so queues stay ordered by latest reply.
  • Team invites, role changes, deactivation, and removal are owner/admin-only; the last active owner cannot be removed.
  • Invites are hashed, single-use, email-bound, and expire.

Platform plans and reconciliation

  • Maximum five platform plans; codes stay stable after creation.
  • Display price, annual/monthly provider IDs, workspace limit, API listing limit, benefits, and recommendation.
  • Paid plans unpublished until an amount + recurring provider price ID pair exists.
  • Read-only platform subscription reconciliation shows state and entitlement without allowing staff drift from the provider.

Platform notifications

Staff-authored notices fan out into per-user inbox rows. Audience can be all accounts or staff only; levels are info, notice, and critical. Delivery counts appear in the operations snapshot.

Audit and exports

  • Append-only audit events with actor, action, organization, target, and metadata.
  • Export jobs support security review and retention workflows.
  • Admin lists expose filtered views, search, and CSV where appropriate.
  • Never place raw secrets, API keys, or provider payloads in audit metadata.

Locale management

Site locales → Manage translations shows bundled and managed locales, coverage, and missing keys. Bundled French is editable through an admin overlay. Import/export supports JSON and TypeScript literals; unknown keys are ignored and reported.

Reference10 min read

API reference

The FastRestful API is a Django REST Framework surface rooted at /api/v1/. OpenAPI is served by the backend and is the authoritative contract.

Base URLs and docs

SurfaceURL
API roothttps://api.example.com/api/v1/
OpenAPI schema/api/schema/
Swagger UI/api/docs/
Health/api/health/live, /api/health/ready
Product hosthttps://{product-slug}.api.example.com
Product path alias/api/v1/{product-slug}/...

Authentication

get tokens
curl --request POST \
  --url 'https://api.example.com/api/v1/auth/token/' \
  --header 'Content-Type: application/json' \
  --data '{"username":"[email protected]","password":"..."}'

# Response
{"access":"...", "refresh":"...", "user": {...}}
authenticated call
curl --request GET \
  --url 'https://api.example.com/api/v1/dashboard/consumer/' \
  --header 'Authorization: Bearer <access>' \
  --header 'X-Organization: <organization-uuid>'

Namespaces

PrefixPurpose
/auth/Token, refresh, register, me, logout, password reset, profile, 2FA, SSO, SAML, invites.
/orgs/, /memberships/, /organization-invites/Workspaces, roles, invites, team.
/products/, /versions/, /endpoints/, /plans/, /categories/Catalog and API lifecycle.
/applications/, /keys/, /subscriptions/Consumer access and entitlements.
/dashboard/Consumer and vendor summary read models.
/gateway/execute/Legacy secure gateway envelope (direct routes preferred).
/request-logs/, /usage-events/, /usage-counters/Metering and observability.
/invoices/, /payouts/, /ledger/, /coupons/, /billing/Money, refunds, provider webhooks.
/support/ (tickets, ticket-messages)Support queue and replies.
/media/, /media-collections/Media library and picker.
/newsletter/Public signup, tracking, preferences.
/short-links/Short links, QR, clicks, health, import/export.
/announcements/Public banner delivery and events.
/platform/Settings, branding, metadata, site content, plans, subscriptions, locales, assets, availability.
/moderation-appeals/, /safety-enforcements/Safety case history and restrictions.
/messages/, /blocks/, /reports/, /reviews/Community and trust.
/audit-events/, /export-jobs/Audit trail and exports.
/webhook-endpoints/, /webhook-deliveries/Signed webhook management.

Core endpoint map

Method + pathPurpose
POST /auth/token/, POST /auth/token/refresh/Password login and rotating token refresh.
POST /auth/register/Create a user and optional first workspace.
GET /auth/me/Session user, organizations, and publish capability.
GET /orgs/, GET /memberships/Workspaces and role membership.
GET /products/, GET /products/{slug}/Public and tenant-visible product catalog.
POST /products/{slug}/import-spec/Import an OpenAPI or Swagger contract into a draft version.
GET /versions/{id}/diff/?against=Compare two release contracts.
POST /versions/{id}/promote/Promote a validated release and retire the previous live version.
GET /products/{slug}/publication-readiness/Return the four publication gates.
GET /applications/, GET /keys/Consumer applications and scoped credentials.
POST /keys/{id}/rotate/, POST /keys/{id}/revoke/Replace or revoke a key safely.
GET /subscriptions/, POST /{id}/cancel/Active plans and cancellation.
POST /subscriptions/{id}/change-plan/Move to another eligible plan.
POST /gateway/execute/Legacy secure gateway envelope.
GET /dashboard/consumer/, GET /dashboard/vendor/Consumer and vendor summary read models.
GET /request-logs/Tenant-scoped, redacted request history.
GET /usage-events/, GET /usage-counters/Metering detail and monthly counters.
GET /invoices/, POST /invoices/{id}/pay/Billing history and provider checkout.
POST /invoices/{id}/refund/Full or partial refund through the original gateway.
GET /payouts/, POST /payouts/{id}/transfer/Vendor settlement and provider transfer.
POST /payouts/{id}/reconcile/Apply strict provider result to a payout.
GET/POST /media/, GET /media/supported-types/Media library and upload validation.
POST /newsletter/subscribe/Public list signup, optionally double opt-in.
GET /short-links/, GET /short-links/{id}/qr/Link management, redirects, and QR assets.
GET /announcements/?path=Best public banner for each placement.
GET /platform/branding/, GET /platform/site-content/Public shell, metadata, navigation, and published content.
GET /platform/plans/, GET /platform/subscription/Platform plans and current entitlement.
GET /platform/locales/, GET /platform/assets/Active locale overlays and custom assets.
GET /tickets/, GET/POST /ticket-messages/Support queue, replies, and request context.
GET /reports/, GET /moderation-appeals/Safety case history and appeal eligibility.
GET /webhook-endpoints/, GET /webhook-deliveries/Outbound webhook configuration and delivery evidence.
GET /audit-events/, GET /export-jobs/Audit trail and export status.

Pagination and filtering

List endpoints use a page-number contract: page, page_size, search, ordering, and resource filters. Responses use {count, next, previous, results}. Ordering accepts a field name, optionally prefixed with -.

example
GET /api/v1/products/?page=2&page_size=25&search=delivery&ordering=-rating_average
GET /api/v1/invoices/?status=paid&ordering=-created_at
GET /api/v1/request-logs/?status_class=server&ordering=-created_at

Error shape

Validation errors use DRF’s field-keyed object. Gateway and platform errors use a JSON object with a machine-readable code, a human message, and optional retry metadata.

typical error
{
  "detail": "Monthly quota exceeded.",
  "code": "quota_exceeded",
  "retry_after": 300
}

Gateway call shape

direct request
GET https://signal-stack.api.example.com/v2/deliveries?limit=10
X-API-Key: fr_live_…
Idempotency-Key: request-8241

{
  "status": 200,
  "latency_ms": 84,
  "content_type": "application/json",
  "data_preview": "...",
  "headers": {...}
}

Webhooks

There are two kinds of webhook in FastRestful. Keep them separate:

KindDirectionPurpose
Vendor outbound webhooksFastRestful → your serverNotify your systems about platform events. Signed with X-FastRestful-Signature: sha256=<hex> and X-FastRestful-Event.
Payment provider webhooksProvider → FastRestfulSettle invoices, subscriptions, refunds, and payouts. Signature schemes differ per provider.
payment provider routes
POST /api/v1/billing/stripe-webhook/
POST /api/v1/billing/paypal-webhook/
POST /api/v1/billing/flutterwave-webhook/
POST /api/v1/billing/paystack-webhook/
POST /api/v1/billing/razorpay-webhook/
  • Vendor webhook endpoints must use HTTPS and store a signing secret.
  • Deliveries are signed, retried safely, and logged with status and response preview.
  • Payment provider webhooks are registered in each provider's dashboard, not in the vendor webhook section.
  • Provider event IDs are unique; duplicate signed events are acknowledged without double-settling.
  • Setup and webhook event lists are in Payment gateways, step by step.

OpenAPI workflow

shell
cd backend
python3.11 manage.py spectacular --file schema.yml --validate

Use the generated schema for SDKs, Postman, or contract tests. Product-level OpenAPI contracts are separate: they are imported into API versions and promoted through the release pipeline.

Getting unstuck

If an endpoint returns an unexpected code, check the request ID, authentication method, workspace header, and subscription state first. The Troubleshooting guide covers the common failure modes in order.