Repository navigation
DID-7740: [SDK-PY] Official Python server SDK: complete API surface, webhooks, Authentication, Transactions, generated types and tests - #1
Merged
Conversation
…webhooks, Authentication, Transactions, generated types and tests Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
…webhooks, Authentication, Transactions, generated types and tests Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
…webhooks, Authentication, Transactions, generated types and tests Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The official Didit Python library (
didit-sdk,import didit) for every Didit server API: sessions, standalone checks, KYB, users and businesses, workflows, webhooks, cases, Transactions, wallet screening, Travel Rule and account management. It has full types, safe webhook verification and an Authentication module, and it regenerates itself fromopenapi/(DID-7740, parent DID-7736).What is in it
src/didit/types,src/didit/resources,tests/generated), produced bycodegen/fromopenapi/only:verification.jsonplus the 9account.jsonoperations, in 50 resources.Didit(sync,httpx.Client) andAsyncDidit(httpx.AsyncClient) come from the same templates.TypedDicts withNOT_GIVEN.User-AgentandX-Didit-Clientheaders, the error hierarchy, the retry policy (POST and PATCH are never retried once they may have reached the server), timeouts, raw and streamed responses, origin pinning for pagination links, redaction andDIDIT_LOG=debug.verifyandverify_parsed, every signature header including the-Previousrotation variants, canonical JSON byte for byte, opt-in Simple signatures and typed events withUnknownEvent. All 25 backend vectors pass.didit-sdk[auth], DESIGN 10):verify_access_tokenandverify_id_token(ES256 only)..github/workflows/spec-sync.yml, DESIGN 3.1): daily, openschore(spec): sync API spec YYYY-MM-DDwith an operation-level change summary.release.ymlwith PyPI trusted publishing, triggered only by a published release.examples/(including FastAPI, Flask and Django webhook receivers), CONTRIBUTING, SECURITY, CODEOWNERS, issue and PR templates, Dependabot and a gitleaks CI job.Review round 3: the requested change
x-didit-sdk(DID-7781) would go red: 12 tests incodegen/tests/test_codegen.pyread names straight fromopenapi/overlay.json, which that sync emptiesx-didit-sdkfirst, then the overlay, as_count_operationsalready did. Tests that change a name change it in that same place. The overlay-drop test sets up its own starting point (naming only in the overlay), so it holds before and after upstream carries the naming. Only this test file changed.Evidence (local):
DID-7781sync in a scratch copy: every overlay entry written upstream asx-didit-sdkin both specs, thencodegen/spec_sync.py --from-dir. Result: no breaking changes, "Dropped 266 overlay entries that upstream now carries", andgenerate.py --checkreports the generated code unchanged.codegen/testswent from 12 failed and 27 passed to 39 passed; the full suite the sync job runs passes (744 passed).codegen/tests39 passed (also on Python 3.10 and 3.14); full suite 744 passed with 98.92 % core coverage; ruff, ruff format, mypy, pyright andgenerate.py --checkclean.Review round 2: every requested change
Blocking
.webpuploaded asapplication/octet-stream_files.guess_content_typeuses a fixed table for every type the API accepts (jpg, png, webp, tiff, heic, pdf, zip, csv, ndjson, ...), the same on every OS, and falls back tomimetypesfor other types. A test simulates a host whosemimetypesknows nothing.APITimeoutErrororAPIConnectionError. Error bodies follow the same rules. Streamed responses (iter_bytes,read,parse,write_to_file) raise the SDK's errors too. Covered by tests (sync and async) and by the real-socket quickstart run below.openapi/deprecated-operations.jsonis now keyed byresource.method. A rename upstream keeps the old name as a@deprecatedalias of the same endpoint untilremove_in, the next minor version, and is listed under "Breaking changes"; its old response type stays importable. Removed operations work as before. A name that moves to another path is compared old against new, and a breaking difference is listed. A name that now belongs to a different operation (swapped or reused) is breaking. An operation that becomes device-only is removed and listed as breaking, because server SDKs never expose those. An operation that loses its naming makes the generator fail until it is named.WebhookVerificationErrorverifyandverify_parsed) and astrbody with a lone surrogate all giveinvalid_payload.secret=None(an unset environment variable), abytessecret or a non-strentry raiseConfigurationErrorwith a precise message. A negativetoleranceis checked before the body.Should fix
httpor another hosttoken_endpointmust be on the issuer's origin, andtoken_endpointandjwks_urimust use https (http only for a loopback test issuer). A discovery document withoutissueris refused (OIDC Discovery 4.3).APIConnectionErrorinstead of fetching again while holding the lock. It never raisesTokenVerificationErrorfor this, so an outage does not look like a bad token. A cancelled call and aConfigurationErrordo not count.AttributeErroron accessNone. It stays out ofmodel_fields_setandto_dict(), andto_dict()/to_json()no longer emit pydantic serializer warnings for off-schema values.oneOf/anyOf, types (integer to number widening is allowed on requests), enum values (breaking on requests, open on responses), nullability and required fields, with direction-aware rules. Removed component schemas are breaking. The reviewer's case (DocumentAIDetectedCode.typefrom string to integer) is now reported as breaking on the three operations that return it. Test counts are derived from the JSON; none is hard-coded.nextlink raisesDiditErrorinstead of looping. A relativenextlink resolves against the page URL, with the same origin check. A non-JSON or empty list response raisesAPIResponseValidationError.default_headers, credentials, per-request headers) replaces a header of any case.Content-Typeis set only when the caller did not set it.Noneremoves a header, including httpx's ownUser-Agent.chore/spec-synctests/test_contract.pyfails once the package version reaches an entry'sremove_in, which forces the deletion in that release PR (documented in CONTRIBUTING).CODEOWNERSused the organisation@didit-protocol/backend. That team needs write access to this repository; see the setup below.Found by a second review of this revision, also fixed
APIResponseValidationErrorand errors from streamed reads. The form body of a token request (code, PKCE verifier, refresh token) is never kept on a request object.amountstring must be a plain ASCII decimal, because it is sent as written. A non-iterable webhook secret is aConfigurationError.Nits: I fixed all of these.
timestampismissing_timestamp, andX-Timestampmust be plain ASCII digits.exp,nbforiattoo large for a float ismalformed.AsyncAuthenticationkeeps one lock per event loop.invalid_grant: <description>.amountmust be a finite decimal.required is False).Authorizationheader on the request they carry.jti.DIDIT_LOGdocuments and acceptsdebugonly.DiditErrorbefore sending.x-api-keyon the request they carry.$refdoes not crash the sync..release-please-manifest.jsonsays0.1.0, like the code.Quickstart against a mocked server
examples/quickstart.pyran unchanged against a local stand-in forverification.didit.meon a real socket. The mock drops the connection partway through the first decision body, and the SDK retries the GET:Tests and checks
CI for this revision runs once it is published; it changes only
codegen/tests/test_codegen.py. CI on the previous head4956888(run 39056465333) is green: lint, both type checkers, the generated-code check, the build, the secret scan and the tests on Python 3.10 to 3.14 (Linux), 3.12 on macOS and 3.12 on Windows (744 passed; this job failed in the previous round). The live smoke job is skipped, as designed.The same checks run locally:
uv lock --checkpyproject.tomluv run ruff check .anduv run ruff format --check .uv run mypy(strict)uv run pyrightuv run python codegen/generate.py --checkuv run pytest -m "not live" --cov --cov-fail-under=90uv build, install the wheel in a clean venv, importcryptography;didit/py.typedshippedactionlinton every workflow.gitleaks.toml(full history and working tree)The 744 tests include:
DiditandAsyncDidit;canonical_v2;Command output tails
Public API tree
All 257 API methods plus the webhook and Authentication entry points
One-time setup before the first release (a person must do this)
didit-sdkproject. The transfer from Agustin Saiz is pending; the fallback name isdidit-python(DESIGN 2.1).didit-protocol, repositorysdk-python, workflowrelease.yml, environmentpypi.pypi: a required reviewer (Alex), limited tov*tags.@didit-protocol/backend) write access to this repository so thatCODEOWNERSrequests its review, or name the owning team there.SPEC_SYNC_TOKENandRELEASE_PLEASE_TOKEN.DIDIT_SANDBOX_API_KEYsecret and theDIDIT_SANDBOX_WORKFLOW_IDandDIDIT_SANDBOX_BASE_URLvariables, for the manual job.mainwith the CI jobs.Release-As: <version>footer (REL-PY uses 1.0.0). Release-please runs only when started by hand, andrelease.ymlonly on a published release, so merging publishes nothing.Merge
Squash-merge this pull request with a conventional title, for example
feat: official Python server SDK (DID-7740). Release-please reads only conventional commits.Not verified here
uv run pytest -m livewithDIDIT_SANDBOX_API_KEY(andDIDIT_SANDBOX_WORKFLOW_ID).nextlinks are not checked. The SDK refuses any origin other thanhttps://verification.didit.me; one live list call with more than one page settles it.Parity: the core fixes in this revision touch behaviour that DESIGN.md describes: retries on body read errors, webhook input errors, pagination guards, Authentication discovery checks and the spec sync deprecations. They are listed in "Follow-ups for the supervisor" on DID-7740, so that the other six SDKs make the same changes.
Full diff: main...botinc/run-a79f155d
🤖 Generated with Claude Code