Bounded Hosted Jobs
Start optional bounded server jobs, inspect retained progress and output, and cancel new work safely.
#Bounded hosted jobs
Hosted jobs start immediately and continue after the HTTP client disconnects or the SDK closes. They are an optional deployment feature, disabled by default. An operator must explicitly enable jobs before callers can start them. This candidate does not establish a published SDK release or hosted availability. Use only the qualified SDK source or the exact QUALIFIED_VERSION described in Getting Started; publication and hosted enablement remain separate release steps.
#Start, inspect and cancel
Every start requires a positive integer step limit, a positive duration and an explicit positive expense ceiling expressed as an exact decimal string. The server ceiling also applies: at most 64 aggregate model/tool steps, two active jobs per project and the configured task deadline, never more than 1800 seconds. Nested models and helpers share the root's steps, expense ceiling and fixed database deadline. Compaction is independently disabled by default; enabling jobs does not enable it. If separately enabled, its work shares those same bounds.
The example requires an enabled deployment, valid runtime credentials and a qualified SDK; starting it can incur model expense.
from maivn import Client, HostedJobOptions, RunOptions
client = Client()
job = client.jobs.start(
['Summarize the supplied research notes.'],
job=HostedJobOptions(
idempotency_key='research-summary-2026-10-22',
max_steps=20,
max_duration_seconds=120,
),
options=RunOptions(max_expense_usd='0.50'),
)
# Save this server-assigned identity for later status reads or cancellation.
session_id = job.session_id
status = client.jobs.get(session_id)
print(status.state, status.terminal_reason, status.steps_used)
# To stop new work when needed:
# status = client.jobs.cancel(session_id)Async applications use await client.jobs.astart(...), await client.jobs.aget(session_id) and await client.jobs.acancel(session_id). Use the async methods inside an active event loop. These operations map to POST /v1/jobs, GET /v1/jobs/{session_id} and POST /v1/jobs/{session_id}/cancel. Start returns status rather than waiting for the final answer. Closing the client does not cancel the job.
#Supported work
Jobs support model calls and free system tools. Registered external worker and webhook tools require an approved immutable agent revision. Add registered_agent={'agent_id': 'registered-agent-id', 'version': 1} to HostedJobOptions to use that revision's pinned instructions, model, output schema, agents, skills, memory and authorized tools. The caller supplies messages, private data, thread context and explicit bounds. Nondefault caller model or policy options with a registered agent are rejected with HTTP 422; default SDK wire values are accepted. Registered jobs use the authenticated API-key owner's identity, replacing a caller-supplied user label.
SDK-local Python tools, human-input continuations, paid hosted search, sandbox and hosted artifact producers are unsupported. Background memory enrichment is skipped. External connector/provider fees charged outside mAIvn are outside max_expense_usd; that ceiling uses provider-expense accounting, not a guarantee about every external bill. See Billing and Usage.
#Safe retries and identity
Repeating the same project idempotency key with the same owner credential and semantic request returns the original job. A different request conflicts with HTTP 409; generated message IDs and timestamps do not change identity. A timeout or lost start response does not prove admission failed. Retry the same request and key to recover its status. A new key can start new work and incur new effects.
An explicit per-call thread takes precedence over the configured client thread. When neither is set, the SDK derives a credential-scoped thread identifier from the idempotency key using a domain-separated keyed digest. The credential itself is never sent as the thread identifier. Keep the same authorized owner key and thread context for retries. Rotating the credential changes the derived default and can conflict with the existing project-wide key. An explicit original thread can preserve thread context when using the original authorized owner key; a new owner key cannot take over that key's job or idempotency key.
client_timezone and sdk_deployment_timezone also participate in request identity. Preserve both when retrying from another host or timezone, or the start may return HTTP 409.
Operators must keep MAIVN_INTERNAL_PLANE_HMAC_SECRET configured and stable across replicas while retained start requests need replay. A domain-separated keyed digest includes private input without storing the raw request. Rotating this server secret makes old start retries conflict, even after the execution limit has passed; retained records can outlive that limit. Reading or cancelling an already known session is unaffected by this secret rotation.
#Durable progress and output
HostedJobStatus provides session_id, state, terminal_reason, deadline_at, max_steps, steps_used, max_expense_usd, progress_position, final_message and result. Retained final output is shielded. Private values remain process-only; status and event replay preserve placeholders instead of restoring private values. Output and progress remain subject to retention.
Read jobs through client.jobs.get(...), including after disconnect or server owner loss. Retained events use the existing GET /v1/sessions/{session_id}/events replay endpoint. The ordinary process-local /result endpoint is not the durable job result API. Job status reads and cancellation require the original active owner key and project access. Session event replay is authorized by project access.
state is one of running, completed, failed, cancelled or budget_exhausted. Inspect terminal_reason for the specific outcome: completed, failed, cancelled, interrupted, execution_outcome_unknown, deadline_exceeded, steps_exhausted, budget_exhausted, start_failed or authorization_revoked. It is None while no terminal reason exists.
Cancellation commits a stop for new work; it cannot undo an external effect already dispatched. Late receipts still settle costs. Server/replica owner loss stops jobs safely, retaining safe progress/output and unknown expense holds. Jobs never automatically resume or replay execution after a crash. Deployments end running jobs unless an independently approved drain is used.
A failed or deadline_exceeded outcome can still include dispatched work whose effects or charges are unknown. Unknown reservations stay held; the label does not prove that no effect occurred. There is no exactly-once execution promise. Reconcile external effects before choosing to start replacement work.
#Validation and API errors
Invalid option shapes raise Pydantic validation errors. Missing max_expense_usd, a caller-assigned session_id, or an invalid job session ID raise ValueError locally. Calling synchronous methods in an active event loop raises AsyncContextError. Typed non-2xx API responses raise MaivnHTTPError; transport timeouts and connection failures may raise httpx.HTTPError subclasses. See Errors and exceptions.
Authentication, request-schema validation, plan admission and billing may return their own existing API errors. In read-only breakglass mode, status reads do not reconcile expiry or mutate job state. Branch on MaivnHTTPError.code and status_code, not on human-readable text. Correct invalid requests before retrying; preserve retry identity for an uncertain start.
For recurring invocation, see Scheduled Invocation. For ordinary connected execution, see Sessions and Streaming.