Types & errors

Everything the package exports: the client, the Page object, the key helpers, and the exception hierarchy that lets you catch the one case you handle and let the rest surface.

Everything exported

Python
1from neuctra_authix import (
2    # client
3    Authix,            # the client
4    NeuctraAuthix,     # alias, matching the JavaScript SDK's class name
5    DEFAULT_BASE_URL,
6
7    # pagination
8    Page, MAX_LIMIT, DEFAULT_LIMIT,
9
10    # keys
11    parse_api_key, mask_key, ParsedKey, PUBLISHABLE, SECRET,
12
13    # errors
14    AuthixError,             # base of everything below
15    ConfigurationError,      # bad client setup — raised before any request
16    NetworkError,            # request never completed
17    AuthixAPIError,          # base for every API error response
18    AuthenticationError,     # 401
19    NoUserSessionError,      # 401, no signed-in user
20    InsufficientScopeError,  # 403, wrong key type
21    PermissionDeniedError,   # 403, not permitted
22    NotFoundError,           # 404
23    ValidationError,         # 400
24    VersionConflictError,    # 409
25    RateLimitError,          # 429
26    ServerError,             # 5xx
27)

All of it lives at the package root. The submodules — client, pagination, keys, errors — are an implementation detail and may move.

The Page object

Python
1page = authix.get_user_data(user_id=user_id, limit=50)
2
3len(page)          # records in this page
4bool(page)         # False when the page is empty
5page[0]            # index into the records
6for r in page: ...  # iterate them
7
8page.data          # list[dict] — the records
9page.has_more      # bool
10page.next_cursor   # str | None — pass back as `cursor`
11page.total         # records in THIS page, not the total that exist
12page.raw           # the untouched response, if you need a field we do not expose
AttributeTypeNotes
datalist[dict]The records in this page.
has_moreboolWhether another page exists.
next_cursorstr | NonePass back as cursor to fetch the next page.
totalintRecords in this page. List endpoints report totalFetched and search endpoints totalItems; both are normalised here.
rawdictThe untouched response payload.

total is not a count of everything

It is how many records came back in this page. There is no cheap count of everything, because counting would mean scanning what pagination exists to avoid. If you need a total, maintain a counter as you write.

ConstantValueNotes
DEFAULT_LIMIT20Applied when limit is omitted.
MAX_LIMIT100A larger limit is clamped, not rejected.

Exception hierarchy

Bash
1AuthixError                    # catch this to catch everything
2├── ConfigurationError         # bad key, missing app_id — no request was made
3├── NetworkError               # timeout, DNS, connection refused
4└── AuthixAPIError             # the API returned an error
5    ├── AuthenticationError            401
6    │   └── NoUserSessionError         401 — endpoint needs a signed-in user
7    ├── InsufficientScopeError         403 — publishable key on a secret route
8    ├── PermissionDeniedError          403 — unverified account, plan limit
9    ├── NotFoundError                  404
10    ├── ValidationError                400
11    ├── VersionConflictError           409 — carries current_version
12    ├── RateLimitError                 429 — carries reset_date
13    └── ServerError                    5xx

The tree is the useful part: catching AuthenticationError also catches NoUserSessionError, and catching AuthixAPIError catches every API failure while still letting a NetworkError through — which you usually want, because the two mean different things.

Exception reference

ExceptionStatusWhat it means and what to do
ConfigurationErrorBad or missing key, missing app_id, both key types passed. Raised at construction, before any request. Fix the setup.
NetworkErrorTimeout, DNS failure, connection refused. The request may never have arrived, or may have succeeded with the response lost. Treat as unknown, not failed — never blind-retry a write.
ValidationError400Rejected as invalid, including filtering on a non-searchable field. Fix the request.
AuthenticationError401Wrong password, or an expired or revoked credential. Show a sign-in error.
NoUserSessionError401The endpoint acts on the signed-in user and nobody is signed in. Send them to login.
InsufficientScopeError403A publishable key on a secret-key endpoint. This is a bug in your integration — move the call to your server. Carries hint.
PermissionDeniedError403Authenticated but not permitted — usually an unverified account or a plan limit. The message says which.
NotFoundError404The app, user or record does not exist under this account. Often a stale id.
VersionConflictError409Someone wrote first; nothing was saved. Carries current_version and expected_version. Re-read, reapply, retry.
RateLimitError429Rate limited, or the monthly quota is exhausted. Carries reset_date — back off until then.
ServerError5xxThe API failed. Retry with backoff, but only idempotent calls.

Every AuthixAPIError also carries status, code, message and the raw payload. Branch on code, never on message text — wording changes, codes are the contract.

Handling them

Python
1from neuctra_authix import (
2    InsufficientScopeError,
3    NetworkError,
4    NoUserSessionError,
5    RateLimitError,
6    VersionConflictError,
7)
8
9try:
10    authix.update_user_data(
11        user_id=user_id, data_id=data_id, data=changes, version=version,
12    )
13
14except VersionConflictError as exc:
15    # The only error routinely worth retrying. Nothing was written.
16    retry_from(exc.current_version)
17
18except NoUserSessionError:
19    # The session expired or was revoked mid-use.
20    send_to_login()
21
22except InsufficientScopeError:
23    # Our bug, not the user's: this call needs a secret key and belongs
24    # on the server. Log it loudly; never show it to a user.
25    logger.exception("wrong key type for this endpoint")
26    raise
27
28except RateLimitError as exc:
29    # Back off until the quota resets.
30    schedule_retry(after=exc.reset_date)
31
32except NetworkError:
33    # The request may never have arrived — or may have been processed
34    # with the response lost. Treat it as unknown, not as failed.
35    mark_unknown()

Do not catch and continue

A swallowed 403 on a write looks identical to a successful save until someone refreshes. If you catch an exception, either handle it or re-raise it — except AuthixError: pass is how silent data loss gets shipped.

Do not forward InsufficientScopeError to users

It describes your server's configuration, not their request. It means your code used the wrong key. Log it as a deployment bug and show the user something generic.

Key helpers

Python
1from neuctra_authix import mask_key, parse_api_key, SECRET
2
3parsed = parse_api_key(os.environ["AUTHIX_SECRET_KEY"])
4
5parsed.type      # "secret"
6parsed.env       # "live"
7parsed.prefix    # "sk_live_a1b2c3d4" — not secret, safe to log
8
9if parsed.type == SECRET and running_on_client:
10    raise RuntimeError("secret key must not ship to end users")
11
12# Safe to print anywhere.
13print(mask_key(key))   # sk_live_a1b2c3d4…9f2c
ExportTypeNotes
parse_api_key(str | None) -> ParsedKey | NoneParses without contacting the API. Returns None if missing or malformed.
mask_key(str) -> strPrefix plus the last four characters, e.g. sk_live_a1b2c3d4…9f2c. Safe for logs.
ParsedKeyNamedTupleFields: type, env, prefix — all non-sensitive.
PUBLISHABLE"publishable"Compare against ParsedKey.type.
SECRET"secret"Compare against ParsedKey.type.

The prefix is not a secret

ParsedKey.prefix is the public lookup handle embedded in the key. It identifies which key was used without exposing it, which makes it the right thing to log when you want to trace a request back to a credential.

Client attributes

AttributeTypeNotes
app_idstrThe app every request is scoped to.
key_type"publishable" | "secret"Which credential this client holds. Useful for an assertion at startup.
key_prefixstrNon-sensitive key prefix, safe to log.
base_urlstrAPI origin, without a trailing slash.
timeoutfloatPer-request timeout in seconds.

Related