To get started, you will need to have a Salesforce account and a connected app.
You will also need to have an OpenStax user account with a matching Salesforce Contact in the environment you are using.
- Salesforce account
- Salesforce connected app with the following permissions:
- Access and manage your data (api)
- Perform requests on your behalf at any time (refresh_token, offline_access)
- Full access (full)
- OpenStax user account or locally running accounts server
- User account with appropriate permissions
- Python 3.11 or later
- Virtualenv (optional but highly recommended)
- Clone the repo
git clone https://github.com/openstax/sfapi- Install the required packages (in a virtual environment)
mkvirtualenv sfapi
pip install -r requirements/dev.txt- Create a
.envfile in the root of the project and add the following environment variables:
SALESFORCE_CLIENT_ID=<your_client_id>
SALESFORCE_CLIENT_SECRET=<your_client_secret>
SALESFORCE_USERNAME=<your_username>
SALESFORCE_PASSWORD=<your_password>
SALESFORCE_SECURITY_TOKEN=<your_security_token>
SSO_COOKIE_NAME="<oxa_env>"
SSO_SIGNATURE_PUBLIC_KEY="<public_key_for_accounts>"
SSO_ENCRYPTION_PRIVATE_KEY="<private_key_for_accounts>"
SENTRY_DSN="<sentry_dsn>" # not required and will cause a lot of noise in Sentry
# Accounts API (for user lookups — salesforce_contact_id, faculty_status, etc.)
ACCOUNTS_URL="https://dev.accounts.openstax.org" # or http://localhost:2999 for local
ACCOUNTS_CLIENT_ID="<oauth_client_id>"
ACCOUNTS_CLIENT_SECRET="<oauth_client_secret>"
# Local dev only — bypass SSO cookie validation and authenticate as this UUID
DEV_USER_UUID="<your_accounts_uuid>"- Run the server
python manage.py runserverThe API is a RESTful API that provides access to Salesforce data and functionality. It is designed to be easy to use and understand.
The API is documented using OpenAPI and can be accessed at /api/v1/docs/.
The API is versioned and the version is specified in the URL. The current version is v1.
Any changes to the API that are not backwards compatible should result in a new version being created.
The API supports two authentication methods:
1. SSO Cookie — Users logged in to OpenStax Accounts are authenticated automatically via the oxa cookie.
2. API Key — For service-to-service access, use a Bearer token in the Authorization header.
To create an API key with all scopes:
python manage.py create_api_key --name="my-key-name" --scopes="read:books,read:info,write:cases"Then use it in requests:
curl -H "Authorization: Bearer <your-api-key>" http://localhost:8000/api/v1/booksAvailable scopes:
read:books— required forGET /api/v1/booksread:info— required forGET /api/v1/infowrite:cases— required forPOST /api/v1/case
Endpoints like /contact and /adoptions require authentication but no specific scope. The /schools endpoint is public.
Super users (SSO users with all scopes) are managed via the Django admin under Super Users.
For local development, you can bypass SSO cookie validation entirely by setting DEV_USER_UUID in your .env to your OpenStax Accounts UUID. This removes the need to run Accounts locally or deal with cookie domain/crypto issues. This only works when DEBUG=True (i.e., runserver).
GET /api/v1/me — Public endpoint that shows your current authentication status. When logged in, enriches the response with data from the Accounts API:
salesforce_contact_id,faculty_status,adopter_statusself_reported_role,school_type,school_locationassignable_user,assignable_school_integrated— flags for personalizing the Assignable journey
When not logged in, includes a debug section with diagnostics:
- Whether the SSO cookie is present
- Whether the signature and encryption keys are configured
- Step-by-step decryption/verification results to pinpoint failures
GET /api/v1/info (requires read:info scope) — Admin endpoint that shows system status including:
sso_config— SSO cookie configuration: cookie name, key presence, key types, and whether the dev bypass is activeaccounts_api— Accounts API connection status: whether OAuth credentials are configured and if token retrieval succeedsapi_usage— Current Salesforce API usage (from SF/limits/endpoint)release_information— Version and environment details
Salesforce data is synced to a local PostgreSQL cache via management commands. The primary command is sync_all, which runs all interdependent syncs in dependency order:
python manage.py sync_all # accounts → contacts → opportunities → adoptions
python manage.py sync_all --force # force full sync of all objects
python manage.py sync_books # books sync independently (no FK dependencies)Kill switch & API usage threshold: Syncs can be paused via the Django admin (SyncConfig). If daily SF API usage exceeds the configured threshold (default 85% of 285k), syncs are automatically skipped. API call counts are tracked per-source per-day in the admin (SF API Usage Log).
Scheduled jobs (via django_crontab):
sync_all— daily at 5:00 AMsync_books— weekly Saturday at 11:45 PMcleanup_logs— weekly Sunday at 3:00 AM
The API is rate limited to prevent abuse. The rate limit is currently set at 5 requests per minute, per user.
If a user exceeds the rate limit, they will receive a 429 status code and a message indicating that they have exceeded the rate limit.
This can be modified in api/api_v1.py by changing the SalesforceAPIRateThrottle class.
The API uses standard HTTP status codes to indicate the success or failure of a request.
The API also returns a JSON object with a message key to provide more information about the error.
A non-logged in user will receive a 401 status code and a message indicating that they need to log in.
The pardot app provides a marketing data health tracker for Pardot/Account Engagement. It syncs marketing assets (campaigns, forms, landing pages, emails, etc.) and Salesforce health metrics into local tables, computes a health scorecard, and serves a camp-themed dashboard.
Dashboard: https://<host>/pardot/ (requires SSO login + SuperUser status)
API: 29 JSON endpoints under /api/v1/pardot/ — briefing, engagement, assets, campaigns, health-score, issues, tasks, etc.
Sync commands:
python manage.py camp_sync # Tier 1: assets + SF health (~20 API calls)
python manage.py camp_sync --scout # Tier 2: + top 500 prospects
python manage.py camp_sync --survey # Tier 3: full prospect + activity sync
python manage.py camp_sync --entities forms,lists # Selective syncConfiguration: Team roster, demerit weights, grade thresholds, and issue templates are managed via Django admin at /admin/pardot/.
Pardot API v5 auth reuses the existing Salesforce database connection. The only additional .env variable needed:
SALESFORCE_PARDOT_BUSINESS_UNIT=<Pardot Business Unit ID>SFAPI is deployed using bit-deployment.