fivexer (Python)
Typed Python client for the Fivexer Platform /v1 routing API. Create workers and tasks;
the platform matches them continuously and notifies you via signed webhooks.
- Sync + async:
FivexerandAsyncFivexer, with identical surfaces - Two credential planes:
Fivexer(workspacesk_key) andFivexerWorker(workerwt_token) - Zero-config deps: only
httpx - Typed: dataclass models, full type hints,
mypy --strictclean - Resilient: automatic retry on
429/5xxhonoringRetry-After, idempotency keys on creates - Observable: every response updates
client.quotafromX-Quota-*headers
Install
pip install fivexer
Quickstart
from fivexer import Fivexer, CreateTask, UpsertWorker
client = Fivexer(base_url="https://api.5xer.com", api_key="sk_test_...")
# Register a worker
client.workers.upsert(UpsertWorker(id="agent_1", tags=["english", "billing"]))
# Create a task — it is queued for matching
task = client.tasks.create(CreateTask(tags=["english", "billing"], priority=90))
print(task.id, task.status) # task_8fk2 queued
# Inspect who got it, and why
client.workers.queue("agent_1")
client.decisions.list(task_id=task.id)
Async is identical (AsyncFivexer), just await each call.
Resource groups
| Group | Operations |
|---|---|
client.tasks |
create create_many check get list cancel accept ack reject complete assign escalate parked scheduled unpark set_priority suggest_workers |
client.tasks.context |
get set clear |
client.tasks.comments |
add list remove |
client.tasks.attachments |
create confirm list download remove upload |
client.workers |
upsert list get patch set_availability queue remove |
client.skills |
create list get patch remove suggest |
client.teams |
create list get patch remove members set_members |
client.join_links |
create list revoke |
client.identities |
list invite resend_invite create update remove |
client.decisions |
list |
client.workflows |
list get save remove run list_runs |
client.runs |
list get steps cancel complete_step fail_step |
client.learning |
status worker_stats preview_weights apply_weights revert_weights feedback reward feedback_bulk reset |
client.notifications.sequences |
list create get update remove |
client.notifications.channels |
list create get update remove |
client.stats() / client.history |
stats() sla_stats() queue_audit() portal() timeseries workers |
client.team / client.breaks |
presence metrics |
history, team and breaks need the control plane; a data-plane-only deployment raises
FivexerApiError with code history_unavailable.
Operator actions
Hand a task to a specific worker, or take a worker off the line:
result = client.tasks.assign("task_8fk2", "agent_2")
result.previous_worker_id # "agent_1" when it was taken from someone
# "Back in ten minutes" — keeps their unaccepted backlog
client.workers.set_availability("agent_1", False)
# "Gone for the day" — requeues the backlog so others inherit it now
released = client.workers.set_availability("agent_1", False, release_backlog=True)
released.released_task_ids # ["task_8fk2", "task_9aa3"]
force=True on assign bypasses the paused/backlog/veto/prior-rejection checks (worker
existence is still enforced).
Rich task data
A task can carry a title, description, free-form context and references — inline at creation or written separately:
from fivexer import CreateTask, TaskReferenceInput, AddComment
client.tasks.create(CreateTask(
tags=["billing"],
title="Refund request",
context={"orderId": "41"},
references=[TaskReferenceInput(url="https://crm.example/o/41", label="Order 41")],
))
client.tasks.comments.add("task_8fk2", AddComment(body="Called the customer back",
worker_id="agent_1"))
# Reserve -> PUT the bytes to object storage -> confirm, in one call
client.tasks.attachments.upload("task_8fk2", pdf_bytes,
filename="receipt.pdf", content_type="application/pdf")
Worker portal plane
A worker works their own queue with a wt_ session token. login() adopts both the token and
the worker id, so later calls need no extra wiring:
from fivexer import FivexerWorker, WorkerLogin
worker = FivexerWorker(base_url="https://api.5xer.com")
worker.login(WorkerLogin(workspace_id="ws_1", worker_id="agent_1", pin="4821"))
queue = worker.queue()
detail = worker.task_detail(queue.task_ids[0])
worker.accept(detail.id)
worker.complete(detail.id, {"refunded": True})
worker.start_break("lunch")
worker.end_break() # None when no break was open — a normal outcome, not an error
worker.metrics_today()
worker.team_presence()
A worker can also sign in without a password at all — through a QR join link, or an emailed invite. Both mint a session, and the client adopts it, so the next call is already authenticated:
from fivexer import AcceptWorkerInvite, JoinWorkspace
worker = FivexerWorker(base_url="https://api.5xer.com")
# QR self-registration: the worker id is generated server-side — show it to them, it is the
# username they type at the PIN screen next time.
result = worker.join(JoinWorkspace(token="<from the QR>", name="Ada", pin="4821"))
result.pending_approval # True -> no work routes until an operator admits them
# Emailed invite: setting the PIN *is* the sign-in.
worker.accept_invite(AcceptWorkerInvite(token="<from the link>", pin="4821"))
worker.refresh() # rotate in place; False means "re-authenticate", not an error
And manage their own shift, skills and notifications:
from fivexer import ChangePin, PushSubscriptionInput, WorkerSkillLevel
worker.me() # who am I, am I on shift, do skills still need setting
worker.set_availability(True) # workers are created off shift — this is what matches them
worker.set_skills([WorkerSkillLevel(skill_id="sk_1", level=4)]) # replaces the whole set
worker.change_pin(ChangePin(current_pin="4821", new_pin="9137"))
worker.metrics_window("30d")
# Web Push. Check config first: `enabled=False` means this deployment has no VAPID keypair,
# and a browser only gives you one permission prompt.
if worker.push_config().enabled:
worker.push_subscribe(PushSubscriptionInput(endpoint="https://fcm/...", p256dh="...", auth="..."))
AsyncFivexerWorker is the awaited mirror. The token is scoped to exactly one worker and cannot
reach task creation or worker management — calling an action before login() raises
worker_id_required locally rather than guessing an id.
Operator onboarding
Getting workers into a workspace, from the sk_ side:
from fivexer import CreateJoinLink, InviteWorkerIdentity, UpsertTeam
client.teams.create(UpsertTeam(key="billing", name="Billing")) # `tag` is derived, and routes
invite = client.identities.invite(InviteWorkerIdentity(email="ada@example.com", label="Ada"))
invite.email_status # 'mailer_unconfigured' is common — then invite_url is the only delivery
invite.invite_url # credential-equivalent until consumed; treat it as a secret
link = client.join_links.create(CreateJoinLink(label="Warehouse hires", max_uses=25))
link.join_url # returned only here — a lost link is re-created, never recovered
Supervisor plane
A crew lead watches and unblocks work rather than doing it. A session is redeemed from a single-use link an owner generated in the console — there is no login and no refresh, so an expired session means "get a new link":
from fivexer import AcceptSupervisorInvite, FivexerSupervisor
sup = FivexerSupervisor("https://api.5xer.com")
sup.accept_invite(AcceptSupervisorInvite(token="<from the link>"))
board = sup.overview() # counts, crew (busiest first) and parked work, in ONE request
board.counts.oldest_wait_ms
board.crew[0].worker_id # the busiest crew member
sup.unpark(board.parked[0].id) # back to the queue
sup.assign("task_8fk2", "agent_1")
sup.set_availability("agent_1", False, release_backlog=True)
AsyncFivexerSupervisor is the awaited mirror. Scope is enforced server-side: a task from
another crew is a 403, not a silent move. session_expires_at is epoch-milliseconds here, not
the ISO string the worker plane uses — the two planes genuinely differ on the wire.
Error handling
Non-2xx responses raise FivexerApiError with the API's code and, on 402/429, the quota
snapshot and retry_after:
from fivexer import FivexerApiError
try:
client.tasks.create(CreateTask(tags=["english"]))
except FivexerApiError as e:
print(e.status_code, e.code) # 429 rate_limited
print(e.retry_after) # 60.0
print(e.quota.task_rate_remaining) # 0
Webhooks
Verify the x-fivexer-signature header (HMAC-SHA256, ±5 min replay window) with the pure
Webhook helper — pass the raw request body:
from fivexer import Webhook
event = Webhook.construct_event(
payload=request.body, # raw bytes
header=request.headers["x-fivexer-signature"],
secret="whsec_...",
)
print(event.event) # task.matched
print(event.data) # { taskId, workerId, ... }
Development
pip install -e ".[dev]"
pytest # 90% line + branch gate is in pyproject.toml's addopts
ruff check src tests
mypy src/fivexer
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fivexer-0.4.0.tar.gz.
File metadata
- Download URL: fivexer-0.4.0.tar.gz
- Upload date:
- Size: 97.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e27157829a26338f7c25af347f0510023858d28a831fe3f6405349f2b98ba3ba
|
|
| MD5 |
6a53c9ea631d6813b1fdd95b24b20bc6
|
|
| BLAKE2b-256 |
d7765b70f1d6ae6bc814dc1e6824c07f5ea76e89f8e1130b8d44f9c4f4dc058b
|
Provenance
The following attestation bundles were made for fivexer-0.4.0.tar.gz:
Publisher:
python.yml on fivexer/sdks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fivexer-0.4.0.tar.gz -
Subject digest:
e27157829a26338f7c25af347f0510023858d28a831fe3f6405349f2b98ba3ba - Sigstore transparency entry: 2545417664
- Sigstore integration time:
-
Permalink:
fivexer/sdks@5d89f7519cad58ed29a7d9e1e24dc36f8e7c39cf -
Branch / Tag:
refs/tags/python-v0.4.0 - Owner: https://github.com/fivexer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python.yml@5d89f7519cad58ed29a7d9e1e24dc36f8e7c39cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file fivexer-0.4.0-py3-none-any.whl.
File metadata
- Download URL: fivexer-0.4.0-py3-none-any.whl
- Upload date:
- Size: 52.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48f91665631d8da707debca5ee2d994d98c82e632eb31f88efab42cbd99bbafb
|
|
| MD5 |
45278064019122149187012134f6cdfd
|
|
| BLAKE2b-256 |
baa18942d346f5a0d772d163fc027c5eb7e9fcb7e8aa4357c2dcff105a1e4091
|
Provenance
The following attestation bundles were made for fivexer-0.4.0-py3-none-any.whl:
Publisher:
python.yml on fivexer/sdks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fivexer-0.4.0-py3-none-any.whl -
Subject digest:
48f91665631d8da707debca5ee2d994d98c82e632eb31f88efab42cbd99bbafb - Sigstore transparency entry: 2545418196
- Sigstore integration time:
-
Permalink:
fivexer/sdks@5d89f7519cad58ed29a7d9e1e24dc36f8e7c39cf -
Branch / Tag:
refs/tags/python-v0.4.0 - Owner: https://github.com/fivexer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python.yml@5d89f7519cad58ed29a7d9e1e24dc36f8e7c39cf -
Trigger Event:
push
-
Statement type: