Skip to main content

onemax

Python client for the OneMax partner API.

OneMax is a 1+1 membership club in Baku. Members pay for a plan and show a code at the counter to use a partner's offer. This library lets a partner's own system do what the counter does: check that someone is a member and record that they used the offer.

Sync and async, fully typed, one dependency.

pip install onemax

Getting a key

  1. OneMax switches API access on for your business.
  2. The owner opens the partner panel, goes to API and creates a key.
  3. The key is shown once. It belongs to one venue and acts there like a cashier.

Keep the key on your server. It must never reach a browser or a mobile app.

Quick start: a member's code

A member opens their OneMax card and reads you the six digit code.

from onemax import OneMaxClient

client = OneMaxClient("omx_live_...")

check = client.verify_code("482915")
if not check.usable:
    print("No discount:", check.reason)
else:
    order = place_order(discounted=True)
    client.confirm_usage(check.usage_id, reference=order.id)

verify_code only answers the question. Nothing counts against the member until you call confirm_usage. If the order falls through, call void_usage(check.usage_id) instead.

Pass your own order id as reference. Confirming again with the same reference returns the same usage, so a retried request is safe.

Account linking

A member links their OneMax account to your app once. After that you check them without a code. Linking is the OAuth 2.0 authorization code flow with PKCE, so the same steps work for a website and a mobile app.

Register the addresses your app may return to in the partner panel, under API, Account linking. Your client id is shown there.

1. Send the member to the permission page.

from onemax import generate_pkce

pkce = generate_pkce()
session["onemax_verifier"] = pkce.verifier

url = client.authorize_url(
    client_id="omx_client_...",
    redirect_uri="https://example.az/onemax/return",
    code_challenge=pkce.challenge,
    state=session_id,
)

In a mobile app, open url in the system browser and use your app link as the redirect_uri, for example taksi://onemax/return.

2. The member returns to your address with code and state, or with error=access_denied if they declined. Check that state is the one you sent.

3. Exchange the code on your server and store the token.

link = client.exchange_code(
    code,
    code_verifier=session["onemax_verifier"],
    redirect_uri="https://example.az/onemax/return",
)
save(user_id, link.link_token)

The code works once and for five minutes.

4. Check the member whenever you need to.

status = client.link_status(link_token)
if not status.linked:
    forget(user_id)

check = client.verify_link(link_token)
if check.usable:
    client.confirm_usage(check.usage_id, reference=order.id)

A member can unlink at any time in their OneMax profile. From then on link_status answers linked=False and verify_link answers reason="not_linked". Neither raises.

You learn only whether the subscription is active. The member's name, email, phone and photo are never shared.

Async

AsyncOneMaxClient has the same methods, awaited:

from onemax import AsyncOneMaxClient

async with AsyncOneMaxClient("omx_live_...") as client:
    check = await client.verify_code("482915")

Results

verify_code and verify_link return a Verification:

Field Meaning
active The member has a subscription that works today
usable They may use the offer right now
reason Why, as a Reason
usage_id Set when usable is true. Confirm it or void it
daily_limit How many times a day a member may use this venue
Reason Meaning
OK Go ahead
NOT_SUBSCRIBED No subscription, or it has ended
LIMIT_REACHED The member already used today's allowance at this venue
OFFER_UNAVAILABLE The venue or the offer is not open right now
INVALID_CODE The code is wrong, expired or already used
NOT_LINKED The link token no longer works

Reason and UsageStatus are string enums, so check.reason == "ok" works as well. A value this version does not know is returned as a plain string.

If a venue has several offers open, pass offer_id. client.context() lists them.

Errors

Everything the library raises is a OneMaxError.

Class When
TransportError No usable answer: a connection failure, a timeout, or a body that is not JSON
ApiError The API answered with an error. Has status, code, message, details, request_id
AuthenticationError 401. The key is missing, wrong or switched off
AccessDisabledError 403. API access is switched off for the partner
NotFoundError 404. No such usage for this key
ConflictError 409. For example usage_limit_reached, already_applied, reference_used
InvalidRequestError 400 or 422. For example offer_required, link_code_invalid
RateLimitError 429. Has retry_after in seconds
ServerError 500 and above
from onemax import ConflictError, OneMaxError

try:
    client.confirm_usage(usage_id, reference=order.id)
except ConflictError as error:
    if error.code == "usage_limit_reached":
        charge_full_price(order)
    else:
        raise
except OneMaxError:
    retry_later(order)

message is in Azerbaijani and meant for your logs. Decide on code.

Limits

  • The member's daily limit applies exactly as it does at a counter.
  • 120 requests a minute per key.
  • The library never retries by itself. confirm_usage with a reference is safe to retry.

Development

uv sync
uv run ruff format --check .
uv run ruff check .
uv run mypy src
uv run pytest

Releasing

Set the version in src/onemax/_version.py, add a ## x.y.z section to CHANGELOG.md, commit, then push a tag vx.y.z. The Publish workflow checks that the tag equals the package version, runs the checks, publishes to PyPI and creates the GitHub release from the changelog section. A tag that does not match the version publishes nothing.

License

MIT

Metadata

Release files for onemax 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for onemax 0.1.1
File Size Uploaded
onemax-0.1.1.tar.gz 64.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for onemax 0.1.1
File Interpreter ABI Platform
onemax-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 76.0 kB

Release files / onemax-0.1.1.tar.gz

Download URL onemax-0.1.1.tar.gz
Size 64.0 kB
Tags Source
SHA-256 checksum
How to use checksums
7f2184e99af951e69cc16272f95fe5ea1f9904374a5212b211371d933a7d2966
BLAKE2b-256 checksum
How to use checksums
2596c8f57dcb147a863f13d57ce46737a54f2e5d91920d468de3ef92ff8d45f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release files / onemax-0.1.1-py3-none-any.whl

Download URL onemax-0.1.1-py3-none-any.whl
Size 11.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
16a7683b4ebb4603775d56546b665c64a0f5afb8129e095914c9e01ed36c6cd5
BLAKE2b-256 checksum
How to use checksums
d8fa15d325836353fa40460616f65b6aabbdb6ff639002b7ffbe7a0f37f87987
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page