Thank you for helping. Read docs/DESIGN.md first: it is the shared contract of all seven Didit server SDKs, and when the code and the design disagree, the code is wrong.
You need uv. It installs the Python version from .python-version and the locked dependencies from uv.lock.
uv sync --frozenRun every command with uv run so that it uses the project environment.
Generated layer: src/didit/types, src/didit/resources and tests/generated.
These files are produced by codegen/ from the OpenAPI documents in openapi/ and start with Code generated by codegen from openapi/*.json. DO NOT EDIT.
Never edit them by hand. To change them, change the generator in codegen/ or openapi/overlay.json, then regenerate:
uv run python codegen/generate.pyCI runs uv run python codegen/generate.py --check, which regenerates into a temporary directory and fails when the result differs from what is committed. A hand edit, or a generator or overlay change committed without regenerating, fails that check.
Core layer: everything else under src/didit (client, transport, retries, errors, pagination, files, webhooks, Authentication). It is hand-written, small and fully tested.
openapi/verification.json,openapi/account.jsonandopenapi/webhook-vectors.jsonare upstream copies. Never edit them by hand; only the spec sync workflow (.github/workflows/spec-sync.yml) updates them.openapi/overlay.jsonholds the SDK naming (x-didit-sdk, DESIGN section 4) for operations whose upstream definition does not carry it yet. Keys are"<METHOD> <path>"forverification.jsonand"account <METHOD> <path>"foraccount.json; each value hasresource(dot path of snake_case names, one nested accessor per segment),method,auth,serverand, for application-scoped paths,scope. When upstream carriesx-didit-sdkfor an operation, the upstream value wins and the overlay entry is deleted.- A new server operation without SDK naming makes the generator fail with a message that names the operation. The fix is an overlay entry (or
x-didit-sdkupstream).
The spec sync runs daily. When upstream changed, it regenerates the code, runs the checks and opens a pull request titled chore(spec): sync API spec YYYY-MM-DD. Its body lists added, removed, renamed and changed operations and schemas; field-level differences (nested fields, types, enum values, nullability, required fields) that can break code written against the SDK are listed under "Breaking changes". A download that is not an OpenAPI 3 document with paths is refused and nothing is written.
When a sync fails a check, the pull request opens as a draft; push the fix (usually an openapi/overlay.json entry) to its branch chore/spec-sync. While that branch carries a commit that is not from the sync, later syncs leave it alone, so the fix is never overwritten: merge or close the pull request and the next sync picks up anything that changed since.
The sync never deletes a public method silently (DESIGN section 3.1). When an operation is removed upstream, or its x-didit-sdk naming changes (a rename), codegen/spec_sync.py records the old method in openapi/deprecated-operations.json, keyed by resource.method, with a dereferenced copy of the operation and remove_in, the next minor version. The generator keeps it as a method marked @deprecated (a renamed method's old name calls the same endpoint as the new one). An operation that moves to another path with the same naming is not deprecated; the summary compares the old and new operation. Two cases are breaking and are not kept: a name that now belongs to a different operation (swapped or reused), and an operation that becomes a device or end-user token operation (server: false), which server SDKs never expose. An operation that loses its naming makes the generator fail until openapi/overlay.json names it again.
- When the release that reaches
remove_inis prepared (the release-please pull request),tests/test_contract.pyfails until the entry is deleted: delete it, runuv run python codegen/generate.pyand push to the release pull request. - If a deprecated name becomes live again (for example an overlay entry gives a new path the old name), the generator says so; delete the entry.
CI runs all of these; run them before you open a pull request.
| Check | Command |
|---|---|
| Lint | uv run ruff check . |
| Formatting | uv run ruff format --check . (fix with uv run ruff format .) |
| Types | uv run mypy and uv run pyright |
| Generated code | uv run python codegen/generate.py --check |
| Tests | uv run pytest |
| Core coverage (90 % or more) | uv run pytest --cov --cov-report=term-missing --cov-fail-under=90 |
| Build | uv build |
Unit tests never touch the network. Fixtures are synthetic: never commit API keys, secrets or real customer data. CI also scans the full git history with gitleaks (.gitleaks.toml).
tests/test_live.py talks to a Didit sandbox application and runs only when DIDIT_SANDBOX_API_KEY is set:
DIDIT_SANDBOX_API_KEY=... uv run pytest -m liveOptional: DIDIT_SANDBOX_WORKFLOW_ID (enables the session test) and DIDIT_SANDBOX_BASE_URL.
They never run in the default CI. A maintainer can start them from the CI workflow (Run workflow) when the repository secret is set.
Pull request titles and commits follow Conventional Commits: fix: is a patch, feat: a minor version, and feat!: or a BREAKING CHANGE: footer a major version (a minor version while the SDK is 0.x). Pick the type that matches the semver impact of the change.
Releases are cut by release-please from those commits. It updates the version and generates CHANGELOG.md; never edit the changelog by hand. Squash-merge pull requests so that their conventional title becomes the commit release-please reads. .release-please-manifest.json holds the released version.
release-please runs on every push to main. Merging the release pull request is the release approval (Alex, 2026-10-29); nobody tags or publishes by hand. The next run creates the vX.Y.Z tag and the GitHub release and starts release.yml, which publishes to PyPI with trusted publishing (no stored token).
Didit has seven server SDKs: sdk-python, sdk-node, sdk-go, sdk-java, sdk-php, sdk-ruby and sdk-dotnet.
- Any public API change in
service-didit-verificationorservice-didit-authmust reach the OpenAPI documents in mintlify-docs, and from there every server SDK through the spec sync pull request. - A change to hand-written core behaviour that DESIGN.md describes (client options, errors, retries, pagination, webhooks, Authentication) is made in all seven repositories, one pull request each, and DESIGN.md is updated in all seven in the same words.
- Feature parity: a capability shipped in one server SDK ships in the other six.
Every pull request states its parity: the same change in the other six repositories, or why it does not apply.
A change to the public SDK surface needs tests, a README update and a conventional commit type that matches its semver impact.
Report vulnerabilities privately, never in an issue. See SECURITY.md.