syntheticcfo - Python client
synthetic cfo generates SAP and Oracle ledgers from accounting rules, with fraud planted, every planted record written down at the moment it is planted, and innocent look-alikes beside nearly every scheme, so fraud detection can be trained and measured without a single real record. Three schemes are closed sets by design, with no look-alike planted: segregation of duties, the ghost vendor and phantom inventory, where the join or the reconciliation that finds them is the real analytic and every match is a genuine exception.
This client generates that data, for SAP ECC, SAP S/4HANA and Oracle Cloud, from a script, a notebook or one command. Every dataset it generates is a four-layer package with its labelled fraud answer key (a blind-track dataset keeps part of the key sealed on the platform for scoring) and a reproducibility certificate that names the engine version and lists the SHA-256 of its files: the same seed and configuration on the same engine version reproduce the package byte for byte, and on another engine version they can build a different dataset. A dataset with purchase to pay, order to cash, payroll, inventory or fixed assets also carries articulated financial statements. No language model ever generates the numbers.
The client has no dependencies beyond the standard library:
pip install syntheticcfo
That installs the client and the syntheticcfo command. If your network blocks
the package index, the platform serves the current wheel itself; the exact
command is on https://app.syntheticcfo.com/evaluate.
Or, since it is one file, take it directly and drop it anywhere:
curl -O https://app.syntheticcfo.com/sdk/syntheticcfo.py
An account and a key
A free account needs no card: all three platforms and the core four cycles, within the free plan's limits on scale and on datasets a month, which https://app.syntheticcfo.com/evaluate states. Make one at https://app.syntheticcfo.com/?signup=1, then create an API key under Account, API access. The key carries your plan and monthly allowance. It cannot manage your account, read your chats or create more keys.
One command
syntheticcfo generate --key scfo_YOURKEY --platform oracle --industry retail --fraud high --rows 600
It builds the world, waits, and saves the package zip where you stand.
python -m syntheticcfo does the same thing everywhere.
| Command | What it does |
|---|---|
syntheticcfo generate |
builds a world, waits, and saves the package zip |
syntheticcfo download JOB_ID |
waits for a job if it is still building, then saves its package |
syntheticcfo reproduce JOB_ID |
runs a job again with its seed and configuration, then saves the new package |
syntheticcfo score JOB_ID FILE |
grades your detector's flagged record ids (a file, or - for standard input) against the job's answer key |
syntheticcfo me |
shows the account the key belongs to: plan, this month's generations, limits |
syntheticcfo jobs |
lists your recent generations, with the engine each ran on and what Reproduce would do |
Every command takes --key (or reads SCFO_API_KEY); generate, download
and reproduce also take --lab and --out. generate also takes:
| Option | What it sets |
|---|---|
--platform |
SAP_ECC (the default), SAP_S4HANA or ORACLE, plain words like oracle work; or a combined environment, SAP_MIGRATION_PAIR or INTERCOMPANY_GROUP, on the Team plan and above |
--modules |
the cycles, any of P2P O2C CE R2R HR MM FA (default: the core four, P2P O2C CE R2R) |
--industry |
TMT (the default), PHARMA, MANUFACTURING, FINANCIAL_SERVICES or RETAIL |
--fraud |
none, low, medium (the default) or high |
--rows |
the scale: base transactions per cycle, the setting the plans call rows (not a count of the rows the tables end up holding), up to your plan's limit (syntheticcfo me shows it). A value outside the platform's range is first brought into it; a value then above your plan's limit is refused with a message naming the limit. Left out, the world takes the standard volume; when your plan allows less, as the free plan does, it is sized at your plan's limit instead and the command prints the platform's note saying so |
--jurisdiction |
a country code such as US, DE, ZA or JP (syntheticcfo generate --help lists them) |
--seed |
any integer; the same seed and configuration on the same engine version rebuild the world byte for byte (on another engine version they can build a different dataset). Left out, a fresh one is drawn and printed at the end |
--years |
1 for a single year; 2 to 5 for a multi-year arc on the Team plan and above (3 at most on the intercompany group; the migration pair is one year) |
--evaluation-mode |
withholds the ground-truth marker columns and moves the annotations out of the shipped data |
--lab |
also saves the JSON-lines lab export: one file per table, the answer key and the dataset card |
--out |
the folder or file path for the downloads (default: where you stand) |
From Python
from syntheticcfo import Client
c = Client(api_key="scfo_...") # or set the SCFO_API_KEY env var
me = c.me()
print(me["plan"], me["generations"]) # your plan and this month's generations
job = c.generate(
platform="ORACLE", # SAP_ECC | SAP_S4HANA | ORACLE
industry="RETAIL", # TMT PHARMA MANUFACTURING FINANCIAL_SERVICES RETAIL
fraud="high", # none | low | medium | high
rows=600, # the scale, up to your plan's limit: me["caps"]["max_rows"]
modules=["P2P", "O2C"], # subset of P2P O2C CE R2R HR MM FA
jurisdiction="ZA",
seed=42,
wait=True, # poll until the package is built
)
package = job.download("out/") # the four-layer audit package (zip)
lab = job.lab_export("out/") # JSONL tables + answer_key.jsonl + dataset card
print(job.insights()) # what was planted, by control
generate also takes volume_tier, company_code (the SAP company code on
every document; the intercompany group keeps its subsidiaries' own codes),
region, size_band, fiscal_year, years, entropy,
fx_share and blind (the blind evaluation track, Team plan and above). What
the platform says when it accepts a request, such as the scale it chose when
no row count was given, is on job.notes and printed unless quiet=True.
For model evaluation, add evaluation_mode=True: the shipped database and
workbooks then withhold the ground-truth marker columns, and the annotation
layer moves to 01_Audit_Evidence beside the answer key, so no column names a
scheme on its own rows. The answer key itself ships in full. A scheme built on
one account (phantom revenue on an invented customer) is still isolated by that
account once a detector finds it.
Score a detector
flagged = my_detector(lab) # your detector's record ids, as the tables spell them
result = job.score(flagged) # graded against this job's answer key
print(result["precision"], result["recall"], result["f1"], result["grade"])
A flag is a record id as the answer key spells it: a document number, or a
composite such as 4500000123/10 for a purchase-order line, which also matches
on the purchase order alone and never on the line number alone. On a multi-year
arc, FY2024:4500000123 grades a flag inside that year; a bare id counts in
every year that labels it. job.score(text=...) takes ids separated by
newlines, commas or spaces, or a CSV with a record_id column (a fiscal_year
column beside it names the year on an arc), as syntheticcfo score does. A
file of that one column works too: the client leaves its header line out of
what it sends. The result carries precision, recall, F1, the grade, the
counts and recall per scheme. More on reading a score:
https://app.syntheticcfo.com/evaluate
Reproduce
print(job.reproduce_note) # what Reproduce will do, before you call it
again = job.reproduce(wait=True) # the same seed and configuration, as a new job
On the engine version the original ran on, Reproduce uses no generation and, for a job that completed, rebuilds the same files. On another engine, or when the platform has no record of the original's engine, the same seed and configuration build a new dataset (its certificate names the engine), which counts as one generation on a metered plan. The platform's note on the new job says which. A job from before the platform stored configurations cannot be reproduced; the platform refuses it and says why.
Notes
- Plan limits apply exactly as in the app. A request for a module, platform or
feature your plan does not include is refused with a 402 naming what is
missing. A scale outside the platform's range is first brought into it,
without a note; a scale then above your plan's limit is refused with a 402
naming the limit. A request with no row count takes the volume tier's size
(the standard tier's unless
volume_tiernames another); when that size is more than your plan allows, as the standard tier's is on the free plan, it is sized at your plan's limit instead and the platform says so. Once the month's generations are spent, a request that would use one is refused the same way until the allowance resets on the 1st; thegeneraterequest that leaves a single generation for the month carries a note saying so. - A generation that fails, or that a server restart interrupts, refunds its metered credit automatically. A generation you cancel still counts toward the month on the free plan.
- The client is MIT licensed (the LICENSE file in the package). Datasets generated on the free plan are for evaluation and non-commercial use and may not be redistributed, resold, or used to train or evaluate a competing product; commercial use needs a paid plan. The free public sample carries its own licence: Free for evaluation and non-commercial research, including publishing results on it with a citation. Not to be redistributed as files, resold, or used to train or evaluate a competing product. Full terms: https://app.syntheticcfo.com/terms
Metadata
Release files for syntheticcfo 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| syntheticcfo-0.2.2.tar.gz | 16.4 kB | With provenance | Details for syntheticcfo-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| syntheticcfo-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for syntheticcfo-0.2.2-py3-none-any.whl |
Total release size: 34.2 kB
Release files / syntheticcfo-0.2.2.tar.gz
| Download URL | syntheticcfo-0.2.2.tar.gz |
|---|---|
| Size | 16.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
00a5287d84af60cd913b0d548555e484bcb2e8c72c2106a286fe2769e9c368d8
|
|
BLAKE2b-256 checksum How to use checksums |
a04ffafbc3e2c0f3e462031bd716f48db1e517cb87a47e73881e3a90c8b79cfe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / syntheticcfo-0.2.2-py3-none-any.whl
| Download URL | syntheticcfo-0.2.2-py3-none-any.whl |
|---|---|
| Size | 17.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef7b71992d0fd25a818d3d8e872d7c41ca4e075febedd9c49578c9da21ddc378
|
|
BLAKE2b-256 checksum How to use checksums |
9b65f5b24635a3c68994b1add24c6f04251ceef1b67cd3e4d5fcd293ea3ae4c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for syntheticcfo 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for syntheticcfo-0.2.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.