Types
Pydantic data models returned by SDK methods. Treat them as read-only — they carry data, not behaviour.
File
The record returned by client.files.upload. Represents an uploaded artifact on the backend; pass it into job-creation methods rather than re-uploading.
class File:
file_id: str
filename: str
size: int # bytes
content_type: str
created_at: datetime
job_id: str | None
is_input: bool
ConvertJob
The lifecycle handle for a turbo-lane conversion job. Returned by client.convert.create / create_and_wait.
class ConvertJob:
job_id: str
status: Literal["queued", "processing", "completed", "failed"]
processor_type: str
progress: int # 0–100
progress_message: str | None
result_files: list[ResultFile] | None
error: JobError | None
retry_count: int # default 0
created_at: datetime
updated_at: datetime
started_at: datetime | None
completed_at: datetime | None
estimated_duration: int | None # seconds, when known
@property
def is_terminal(self) -> bool # True when status is "completed" or "failed"
Convention follows OpenAI / Stripe: the model carries data only; behaviour lives in the resource class. Use methods on client.convert to act on a ConvertJob (poll, download, etc.).
ResultFile
A single output file produced by a completed job.
class ResultFile:
filename: str
size: int
mimetype: str
url: str # presigned download URL
JobError
Structured error envelope attached to a failed job. Mirrors the wire format.
class JobError:
code: str
message: str
GoalJob
Lifecycle handle for a goal-lane (multi-step workflow) job.
class GoalJob:
job_spec_id: str
status: Literal[ # full goal-lane lifecycle (13 states)
# non-terminal
"draft", "created", "analyzing", "slots_pending",
"ready", "ready_with_preview", "confirmed", "queued", "executing",
# terminal
"completed", "partial", "failed", "cancelled",
]
progress: int # 0–100, default 0
item_version: int | None
attempt_id: str | None
goal_text: str | None
workflow_id: str | None
file_ids: list[str]
pending_slots: list[PendingSlot] # populated when status == "slots_pending"
filled_slots: dict[str, object]
pending_interrupts: list[dict[str, object]] # opaque HITL union (slot_fill / batch_review / …)
agent_message: str | None
error_message: str | None
error_code: str | None
created_at: datetime
updated_at: datetime
started_at: datetime | None
completed_at: datetime | None
@property
def is_terminal(self) -> bool # status in {completed, partial, failed, cancelled}
@property
def needs_input(self) -> bool # status == "slots_pending"
GoalJob carries no result_files field — produced artifacts are surfaced through the goal-lane event stream and the workflow's own outputs, not on the job handle.
GoalEvent
A single event from a goal-lane event stream (received via the WebSocket transport when iterating client.goals.events(job_spec_id) — async only).
class GoalEvent:
type: Literal[ # 13 event types
"tool_started", "tool_finished",
"agent_step_started", "agent_step_finished",
"orchestration_transition",
"status", "progress", "completed", "failed",
"slot_needed", "keepalive",
"agent_text", "agent_text_done",
]
schema_version: int
job_spec_id: str
emitted_at: datetime
seq: int # default 0
data: dict[str, Any]
@property
def is_terminal(self) -> bool # type in {completed, failed, cancelled}
PendingSlot
A human-in-the-loop interrupt point on a goal-lane job. When a workflow reaches a slot, it pauses and surfaces the question through pending_slots on the GoalJob.
class PendingSlot:
slot_id: str
slot_type: str
question: str
options: list[str | dict[str, str]] | None # null when free-text
required: bool # default True
is_disambiguation: bool # default False
suggested_value: object | None
suggested_confidence: float | None
Resolve a slot by calling client.goals.fill_slot(job_spec_id, slot_id=..., value=...).
Community workflow models
Returned by client.workflows.* (marketplace surface).
Workflow
Full detail row for one workflow.
class Workflow:
workflow_id: str
owner_id: str | None
spec_id: str # use as `source_spec_id` when forking
source_spec_id: str | None
source_type: str | None # "curated" | "user" | "blank"
name: str
description: str | None
visibility: Literal["private", "public", "archived"]
tags: list[str]
stats: WorkflowStats
item_version: int # use for optimistic-lock PATCH
WorkflowSummary
Light-weight community-list row (no system_prompt / spec_json). Returned in pages from client.workflows.search.
WorkflowSearchPage
class WorkflowSearchPage:
items: list[WorkflowSummary]
cursor: str | None # pass to next search() call
WorkflowStats
class WorkflowStats:
run_count: int
fork_count: int
like_count: int
LikeResponse
Returned by client.workflows.like(workflow_id) (toggle).
class LikeResponse:
liked: bool # post-call state
like_count: int
Billing / quota models
Returned by client.account.*.
Plan
class Plan:
tier: Literal["free", "pro"]
CostEstimate
Full response from client.account.get_quota(tools=..., max_iterations=...).
class CostEstimate:
estimated_micro_u: int # legacy single-number summary (back-compat)
estimated_usd: float # rough USD equivalent
estimated_total_micro_u: int # prefer this for the projected total
estimated_min_micro_u: int # range floor
estimated_max_micro_u: int # range ceiling
tools: list[ToolCostEstimate]
quota_check: QuotaCheck # verdict against your tier
Prefer the explicit estimated_min_micro_u / estimated_total_micro_u / estimated_max_micro_u triple over the legacy estimated_micro_u summary.
QuotaCheck
class QuotaCheck:
state: Literal["ok", "soft_limit", "quota_exceeded"]
tier: Literal["free", "pro"]
estimated_micro_u: int
threshold_micro_u: int
upgrade_url: str | None # in-app pricing CTA when not "ok"
ToolCostEstimate
Per-tool row inside CostEstimate.tools.
class ToolCostEstimate:
tool_name: str
per_invocation_micro_u: int
Type usage notes
- All models are Pydantic —
.model_dump()gives a JSON-safe dict,.model_dump_json()gives a string. - Fields typed as
datetimeare timezone-aware (UTC) and serialize as ISO 8601. - Every model is
frozen=True(immutable) — you can't reassign fields on a returned instance. - The marketplace, billing, and
GoalEventmodels addextra="allow"for forward-compat — when the backend adds a new wire field, your existing code keeps working. The core job/file models (File,ResultFile,JobError,ConvertJob,PendingSlot,GoalJob) do not, so an unexpected field there is a hard validation error rather than silently tolerated. - The SDK does not mutate returned models. If you need to retain a snapshot, store the model itself (not a derived dict).
- PEP 561: the SDK ships a
py.typedmarker, so mypy / pyright / pylance pick up these types automatically.