This page records operational behavior that affects production integrations. It is not a sending or authentication walkthrough.
TransportConfig
TransportConfig is used only when the SDK creates the underlying httpx
client. If you pass http_client, that supplied client owns HTTP timeout, pool,
TLS, proxy, trust_env, HTTP/2, custom transport, and event-hook settings.
TransportConfig(
timeouts=TimeoutConfig(),
pool=ConnectionPoolConfig(),
retry=RetryConfig(),
tls=TlsConfig(),
proxy_url=None,
trust_env=True,
http2=True,
auto_refresh_tokens=True,
error_format="problem-details",
)| Field | Type | Default | Passed to |
|---|---|---|---|
timeouts | TimeoutConfig | TimeoutConfig() | httpx.Timeout |
pool | ConnectionPoolConfig | ConnectionPoolConfig() | httpx.Limits |
retry | RetryConfig | RetryConfig() | SDK retry middleware |
tls | TlsConfig | TlsConfig() | httpx TLS verification |
proxy_url | `str | None` | None |
trust_env | bool | True | httpx environment-variable behavior |
http2 | bool | True | httpx HTTP/2 flag |
auto_refresh_tokens | bool | True | Access-token refresh of authenticated clients, see Access-token refresh |
error_format | Literal["problem-details", "legacy"] | "problem-details" | X-Error-Format header on KSeF API requests, see Error format |
auto_refresh_tokens and error_format do not configure httpx, so they
apply even when you pass your own http_client.
TimeoutConfig
TimeoutConfig controls one HTTP request. It does not control how long the SDK
polls KSeF workflow state.
| Field | Default | Meaning |
|---|---|---|
connect | 5.0 | Time allowed to establish a connection. |
read | 30.0 | Time allowed while waiting for response bytes. |
write | 30.0 | Time allowed while sending request bytes. |
pool | 5.0 | Time allowed while waiting for a connection from the pool. |
ConnectionPoolConfig
| Field | Default | Meaning |
|---|---|---|
max_connections | 100 | Maximum open connections. |
max_keepalive_connections | 20 | Maximum idle keep-alive connections. |
keepalive_expiry | 30.0 | Idle keep-alive expiry in seconds. |
TlsConfig
| Field | Default | Meaning |
|---|---|---|
verify | True | Passed to httpx unless ca_bundle_path is set. |
ca_bundle_path | None | CA bundle path used as the httpx verify value when provided. |
RetryConfig
RetryConfig controls SDK retry middleware. It applies to transport errors and
retryable HTTP responses for retryable request types.
| Field | Default |
|---|---|
max_attempts | 3 |
initial_delay | 0.5 |
max_delay | 4.0 |
max_retry_after | 120.0 |
backoff_multiplier | 2.0 |
retryable_status_codes | (429, 502, 503, 504) |
Retry delay is exponential:
min(initial_delay * backoff_multiplier ** (attempt - 1), max_delay)max_delay caps only the exponential backoff. A Retry-After header (seconds
or HTTP-date form) is honored exactly when it is at or below
max_retry_after. When it exceeds the ceiling, the SDK does not sleep and does
not retry: it gives up immediately and raises KSeFRateLimitError carrying the
true header value, so the app can reschedule. The ceiling keeps a multi-hour
Retry-After from stalling a process forever.
The ceiling bounds one sleep, not the whole request. If KSeF repeats the same
Retry-After on every attempt, a single request waits up to
(max_attempts - 1) * max_retry_after before it gives up. With the defaults
that is 240 seconds, because the SDK sleeps 120 seconds twice. Lower
max_attempts or max_retry_after when a request handler cannot afford that.
Retryable requests
The SDK retries only request shapes that are safe enough to repeat at the transport layer.
| Request kind | Retry behavior |
|---|---|
GET | Retryable. |
DELETE | Retryable. |
Most POST requests | Not retried automatically. |
Selected POST requests | Retried automatically when listed below. |
Retryable POST groups:
| Area | Operations |
|---|---|
| Authentication | challenge, refresh token |
| Invoices | metadata query |
| Certificates | query, retrieve |
| Permissions | personal, authorization, EU entity, person, subordinate entity, and subunit queries |
Invoice submission, permission grants, certificate enrollment, token generation, online-session opening, and batch-session opening are not retried by the SDK retry middleware. Persist returned references and resume by status instead of recreating those operations blindly.
Authentication token redemption is also never retried because KSeF returns its
access/refresh pair only once. A lost redemption response raises
KSeFAuthTokenRedemptionError with outcome_ambiguous=True; do not repeat the
redemption automatically.
Access-token refresh
Authenticated clients refresh their access token themselves, using the refresh
token from authentication or from the AuthenticationResumeState they were
resumed from.
| Trigger | Behavior |
|---|---|
| Proactive | A request that finds the access token within 60 seconds of access_token_valid_until first refreshes it, then is sent with the new token. |
| Reactive | A 401 response triggers one refresh and one retry of the same request. A second 401 is raised as KSeFAuthError. |
| Concurrency | Requests share a single refresh: a lock in Client, an asyncio.Lock in AsyncClient. |
| Other statuses | 403 and every other error are not refreshed. |
One refresh is not a full re-login: the SDK replays the original request once
with the refreshed token. If that replay gets another 401, the
KSeFAuthError reaches the app, and the app decides whether to authenticate
again or surface the failure.
auth.auth_tokens, auth.access_token and auth.resume_state() always return
the current tokens, so persist resume_state() again after long-running work.
When the refresh token is expired, or KSeF rejects it, the request raises
KSeFAuthenticationExpiredError, a subclass of KSeFAuthError. The only
remedy is to authenticate again. If the refresh token is already expired but
the access token still has time left, requests keep using the access token
until KSeF rejects it. Transport failures and other errors raised by the
refresh call propagate unchanged.
Refresh never applies to the one-shot authentication redemption or to presigned external-storage transfers (batch part uploads, export downloads). They use separate paths and carry no bearer token.
To manage tokens yourself, turn the behavior off. Expired tokens then surface
as KSeFAuthError and client.authentication.refresh() stays available.
client = Client(Environment.PRODUCTION, transport_config=TransportConfig(auto_refresh_tokens=False))Workflow polling timeouts
Polling helpers use timeout and poll_interval arguments. Those values are
workflow deadlines, not HTTP request timeouts. When a polling timeout is raised,
the remote KSeF workflow may still finish later.
| Workflow | Timeout exception | Identifier carried by exception |
|---|---|---|
| Authentication polling | KSeFAuthPollingTimeoutError | reference_number |
| Token activation/status polling | KSeFTokenStatusTimeoutError | reference_number |
| Metadata visibility polling | KSeFInvoiceQueryTimeoutError | timeout only |
| Direct invoice download readiness | KSeFInvoiceDownloadTimeoutError | ksef_number |
| Online invoice processing | KSeFInvoiceProcessingTimeoutError | invoice_reference_number |
| Export package readiness | KSeFExportTimeoutError | reference_number |
| Permission grant or revoke | KSeFPermissionOperationTimeoutError | reference_number |
| Certificate issuance | KSeFCertificateEnrollmentTimeoutError | reference_number |
| Batch session completion | KSeFBatchSessionTimeoutError | reference_number |
| Online session completion | KSeFOnlineSessionTimeoutError | reference_number |
Resume polling with stored identifiers after a timeout. Do not infer that the remote operation failed only because the local wait deadline expired.
Rate limits
KSeF 429 responses are classified as KSeFRateLimitError. The exception
exposes:
| Attribute | Meaning |
|---|---|
retry_after | Seconds from Retry-After (seconds or HTTP-date form) when KSeF returned the header; otherwise None. |
status_code | Always 429. |
response | Parsed KSeF error payload when available. |
For a 429, the retry middleware honors Retry-After exactly when the value
is at or below max_retry_after, sleeping and retrying up to max_attempts.
When the header exceeds the ceiling, the middleware gives up immediately: no
sleep, no retry, and the request fails with KSeFRateLimitError whose
retry_after is the true header value. The ceiling bounds each sleep, so a
repeated header can hold one request for (max_attempts - 1) * max_retry_after,
which is 240 seconds with the defaults.
What the app still owns: a KSeFRateLimitError means the SDK already gave up,
so schedule the work for later instead of retrying blindly. Use retry_after
to schedule background work. In request handlers, prefer returning or
enqueueing retryable work over sleeping inside the request.
Secret and logging boundaries
Do not log:
- access tokens;
- refresh tokens;
- KSeF token values;
- private keys;
- PEM or PKCS#12 passwords;
- raw invoice XML unless your retention policy explicitly allows it;
- serialized online or batch session state.
Useful production log fields:
| Field | Meaning |
|---|---|
environment | test, demo, or production. |
workflow | Application-level operation name. |
session_reference_number | Online or batch session reference. |
invoice_reference_number | Per-invoice session reference. |
ksef_number | Final KSeF invoice number after acceptance. |
operation_reference_number | Token, permission, certificate, auth, or export operation reference. |
sdk_error_code | KSeFException.context["code"] when present. |
ksef_code | Raw KSeF error code from KSeFApiError.ksef_code when present. |
trace_id | KSeF trace ID from KSeFApiError.trace_id; quote it when contacting KSeF support. |
Resumable workflow state
Most KSeF workflows start with a reference and finish through polling or later download. Store the reference before waiting.
| Workflow | Persist before waiting |
|---|---|
| Authentication | authentication operation reference and temporary authentication-token lifetime |
| Online invoice sending | session reference and invoice reference number |
| Batch upload | batch session reference, upload references, package metadata, and batch state while needed |
| Invoice export | export reference number and protected export handle material |
| Token generation | token reference number and returned token secret |
| Permission grants/revokes | permission operation reference number |
| Certificate enrollment/revoke | certificate operation reference or certificate serial number |
Related reference
- Client lifecycle: Review constructors, branch ownership, close behavior, and lifecycle errors.
- Error handling: Inspect SDK exception classes, KSeF API errors, and timeout handling.
- Client setup: Apply these settings in runnable sync and async client setup examples.
- Status and UPO: Understand status surfaces, UPO documents, polling, and local deadlines.
