Skip to main content

Overview

sg-vivenu-sdk and sg-intacct-xml-sdk follow the same conventions. A new SDK must follow them too. When an SDK has to differ, its README says so.
Written in October 2026. This page is the canonical contract. Both SDKs follow it on main; the fixes made after sg-vivenu-sdk v0.8.0 and sg-intacct-xml-sdk v0.5.0 are listed under Unreleased in each changelog until their next release. Each repository’s docs/agents.md summarises the contract with that SDK’s specifics, and a test in each repository fails if its behaviour drifts.

Construction

Use a different prefix for each hub, company or entity, for example VIVENU_UK_ and VIVENU_DE_. When credentials come from a secret manager, pass them to the constructor instead.

Retries

Both SDKs take retry_policy=RetryPolicy(...) with the same fields. Defaults differ by vendor (see Where the SDKs Differ). vivenu allows 3 attempts by default. Intacct does not retry unless you pass a policy. When a retry wait cannot fit within the remaining request deadline, the SDK stops retrying instead of waiting past the deadline.
By default, neither SDK resends a write unless it knows the write did not run. rate_limited_writes=True is the one opt-in exception: it resends writes rejected with HTTP 429, and neither vendor documents that a rate-limited write was not applied, so a resend can duplicate it. Intacct resends with uniqueid=true, so Intacct rejects a duplicate function; vivenu has no equivalent. See Where the SDKs Differ for each SDK’s exact cases.

Errors

Every SDK error derives from one base class (VivenuError, IntacctError) and carries: outcome_unknown is False for anything the SDK detects before sending, for requests that only read, and for known rejections. Intacct’s legacy readMore is the exception among reads, because a lost response may already have advanced its cursor. An HTTP 429 on a write is not a known rejection in either SDK. Once any attempt of a write may have run, for example a write retried after HTTP 429, every later error of that request reports True. That way one handler can decide whether a failed write needs reconciliation:
Other shared rules:
  • Exception messages never contain payload values, customer data or credentials. Vendor detail is on attributes, such as vivenu’s APIError.message or Intacct’s GatewayError.errors.
  • DeadlineExceededError subclasses TransportError. A deadline that expires before anything is sent reports outcome_unknown=False.
  • Both SDKs decide whether a request was sent from httpcore’s trace stages: a request counts as sent once its headers start going out. A failure or cancellation before that reports outcome_unknown=False. A transport that reports no stages, such as a mock or a custom transport, counts as sent. So does an httpx PoolTimeout, because httpcore reports no stages while a request waits for a pooled connection. Size httpx.Limits for your write concurrency, especially when several clients share one httpx.Client.
  • Hitting a pagination page limit raises PaginationLimitError, which reports outcome_unknown=False.

Diagnostics

Pass on_request=callback to observe every HTTP attempt. The callback receives a RequestEvent with these shared fields. Intacct also emits events that are not HTTP attempts; see Where the SDKs Differ. Events never contain credentials, payloads or response records. Keep callbacks fast, and do not call back into the same client.

HTTP

  • SDK-owned HTTP clients ignore proxy environment variables. To use a proxy, custom transport or instrumentation, pass your own http_client.
  • Redirects are never followed.

Testing

Each SDK ships a public testing module, so applications can test offline without writing a fake server: Mocks never reach the network. Unregistered requests fail.

Versions and Releases

  • _version.py is the single source of the package version, __version__ and the User-Agent.
  • CHANGELOG.md keeps an Unreleased section. A release renames it to the version.
  • Releases are git tags v<version> with a GitHub release, created by the repository’s Release workflow. Dispatch it from main with the version and create_release turned on. With create_release off, it only builds and checks.
  • The SDKs are private and are never published to public PyPI.
  • Consumers pin a tag (see Install From a Release Tag).

Repository Checks

Every SDK repository runs at least these checks in CI on Python 3.11 to 3.14. An SDK can add more; vivenu also checks its generated code, public API baseline and docs build.
  • ruff check and ruff format --check
  • mypy in strict mode on the package
  • strict mypy and pyright on consumer usage files in tests/typing
  • the test suite
  • a build, plus a smoke test of the installed wheel
Workflow actions are pinned to commit SHAs, and CI runs with a read-only token.

Where the SDKs Differ

Each README lists its deliberate differences. The main ones:
Last modified on October 12, 2026