Marketo SSFS (Self-Service Flow Action) provider — a FastAPI service that plugs into Marketo Smart Campaigns as a custom flow-step action, assigning a random name/email to a lead via the async callback contract.
New to this repo? See AUTH_AND_ENDPOINTS.md for a plain-language walkthrough of how auth works, how to use the OAuth client ID/secret, and what each endpoint does.
.
├── main.py # app entry, wires routers
├── auth_methods/
│ ├── auth.py # Basic auth check
│ ├── api_key.py # x-api-key check
│ ├── oauth.py # OAuth2 client-credentials issuance/validation
│ ├── oauth_router.py # POST /oauth/token
│ └── optional_auth.py # accepts Basic, x-api-key, or Bearer for SSFS routes
├── utils/
│ └── users.py # random name/email helpers used by the SSFS callback
├── ssfs/
│ ├── routes.py # Marketo flow action (Adobe path names)
│ ├── default_routes.py # optional SSFS endpoints (icons, instructions, picklist)
│ ├── security_schemes.py # shared OpenAPI security scheme definitions
│ ├── openapi_loader.py # loads/patches ssfs/openapi.json at request time
│ └── openapi.json # SSFS spec for Marketo install
├── scripts/
│ └── validate_ssfs.py # validates ssfs/openapi.json against Adobe's schema
├── tests/
├── .env.example
└── requirements.txt
| Route | Auth | Purpose |
|---|---|---|
GET /health |
none | Liveness probe |
GET / |
none | Links to docs and Marketo install URL |
GET /install |
none | Marketo SSFS OpenAPI spec |
POST /oauth/token |
none | Issues a Bearer token (client-credentials grant) |
GET /brandIcon |
none | Provider brand logo shown in Admin → Service Providers |
GET /serviceIcon |
none | Flow-step icon shown in the Campaign Flow Palette |
GET /getServiceDefinition |
Basic/apiKey/Bearer* | SSFS service definition |
GET /status |
Basic/apiKey/Bearer* | SSFS health for Marketo nightly poll |
POST /submitAsyncAction |
Basic/apiKey/Bearer* | SSFS async flow step + callback |
GET /providerInstructions |
Basic/apiKey/Bearer* | HTML setup instructions |
POST /getPicklist |
Basic/apiKey/Bearer* | Picklist choices for flow parameters |
*Auth is enforced only when the corresponding env vars are set (MARKETO_USER/MARKETO_PASSWORD,
MARKETO_API_KEY, or OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET). /brandIcon and /serviceIcon are
always public per the Adobe SSFS spec — Marketo renders the flow-step palette entry from them.
Requires Python 3.13 (see .python-version).
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
cp .env.example .env # optional
uvicorn main:app --reload --port 8088- Swagger UI: http://localhost:8088/docs
- App OpenAPI (all routes): http://localhost:8088/openapi.json
- Marketo install spec: http://localhost:8088/install
Copy .env.example to .env. All are optional for local dev.
| Variable | Purpose |
|---|---|
SSFS_AUTH_MODE |
Which scheme is listed first in /install security: basic (default), apiKey, or oauth2 |
MARKETO_USER / MARKETO_PASSWORD |
Basic auth credentials for SSFS routes |
MARKETO_API_KEY |
API key for the x-api-key header |
OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET |
OAuth2 client-credentials for POST /oauth/token |
API_BASE_URL |
Overrides servers.url in /install and /openapi.json |
SSFS_BRAND_ICON_PATH / SSFS_SERVICE_ICON_PATH |
Custom SVG/PNG paths for Marketo UI icons |
SSFS_PROVIDER_INSTRUCTIONS_PATH |
Custom HTML for /providerInstructions |
curl http://localhost:8088/install
curl -u marketo:secret http://localhost:8088/getServiceDefinition
curl -u marketo:secret http://localhost:8088/status.replit and replit.nix are checked in so you can fork this repo straight into Replit and get a public HTTPS URL.
- Import this repo into Replit (New Repl → "Import from GitHub").
- Open the Secrets panel and add whichever auth env vars you plan to use — at minimum
MARKETO_USERandMARKETO_PASSWORD(orMARKETO_API_KEY, orOAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET). Also setSSFS_AUTH_MODEif you want a scheme other thanbasicshown first in/install. - Set
API_BASE_URLin Secrets to your Repl's public URL (e.g.https://your-repl.your-username.repl.co) so/installadvertises the right server URL to Marketo. - Hit Run. The first boot installs deps into
/tmp/venv(that step is skipped on subsequent runs, so restarts are fast). - For a stable production URL, click Deploy — that uses the
[deployment]block in.replitto run on Cloud Run with 2 workers.
Once it's up, https://<your-repl-url>/install is what you paste into Marketo Admin → Service Providers.
Why the extra install step vs. other Replit examples: this project uses pip + requirements.txt. Some public reference implementations (like tyron-pretorius/marketoSSFS) use Poetry + pyproject.toml instead — Replit auto-installs Poetry deps as part of the Nix env, so their run = line has no install step. Here you'll see a one-liner in .replit that bootstraps a venv under /tmp/venv on first Run and reuses it after; that's the pip equivalent of what Replit does for free with Poetry projects.
Heads up: Replit tends to shift things around over time (image contents, PORT env behavior, deploy workflow). If the Run button fails, check the Replit shell for the actual error — nine times out of ten it's either a missing Secret (401 on Marketo routes) or python3 -m ensurepip --upgrade needing to run once manually to bootstrap pip. The [deployment] build step in .replit handles both automatically.
Adobe Self-Service Flow Actions require fixed path names (/submitAsyncAction, etc.) and a validated OpenAPI file. That contract lives in ssfs/ — the rest of the app is normal Python.
- Deploy the app somewhere publicly reachable over HTTPS.
- Set
MARKETO_USER/MARKETO_PASSWORD(orMARKETO_API_KEY, orOAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET). - In Marketo: Admin → Service Providers → Add New Service.
- Install URL:
https://<your-host>/install - Enter the matching credentials when prompted.
- In a Smart Campaign's flow editor, add the Random User Email flow step.
The flow step assigns each lead a random generated_email and generated_name via the SSFS callback.
Async contract: POST /submitAsyncAction returns 201 immediately and POSTs the
result to Marketo's callbackUrl in a background task (with x-api-key and
x-callback-token headers). Synchronous invocation is not supported per the
SSFS spec — failed callbacks are logged but do not change the 201 ack.
source .venv/bin/activate
pytestTests cover: /install shape + server-URL rewrite, auth gates for Basic/apiKey/Bearer
(401 when wrong, 200 when valid, bypassed when env vars unset), required-field
validation on /submitAsyncAction, async 201 ack, callback delivery with
correct headers + body (asserted via respx), 201 contract holding when
the callback target returns 500 or is unreachable, and the Adobe SPI schema
validation in scripts/validate_ssfs.py.
Validate the spec locally (optional):
git clone https://github.com/adobe/Marketo-SSFS-Service-Provider-Interface.git
cd Marketo-SSFS-Service-Provider-Interface
npm install
node -e "require('./scripts/validate').validateFile('$(pwd)/../fastapi-sample/ssfs/openapi.json', './schema.yaml')"