Skip to content

Simple API

The additive simple clients accept ordinary Python values, validate them before I/O, and delegate every request to the complete typed client. Use .raw on a client, service, record, or collection when you need provider-specific fields or an operation not yet covered by a convenience helper.

Choose the right layer

Need Use
Common reads, sends, or one-resource lifecycle calls SimpleQuestBlue
Exact request/response models and every provider field QuestBlue
A provider operation missing from a convenience helper simple.raw or simple.service.raw
Previewed, correlated, multi-step provisioning simple.workflows

The simple and workflow APIs are additive: they delegate to the typed 1.0 implementation rather than replacing it.

Account and inventory

from questblue import SimpleQuestBlue

with SimpleQuestBlue() as qb:
    balance = qb.account.balance()
    numbers = qb.numbers.list(number="919", per_page=100)

Collection helpers fetch all available pages by default where the typed API exposes pagination. Pass all_pages=False and page= to number or fax inventory helpers to inspect one provider page; the typed pages used to build a collection remain available through result.raw.

Discovery

Inventory search never purchases a result:

with SimpleQuestBlue() as qb:
    candidates = qb.numbers.search(zip_code="27513", limit=5)
    fax_candidates = qb.fax.search(number_type="local", zip_code="27513")

History

Call and fax history are automatically collected and return immutable records whose .raw property retains provider-added fields:

with SimpleQuestBlue() as qb:
    calls = qb.reports.calls(period="today", number="+1 919 555 0100")
    faxes = qb.reports.faxes(numbers="+1 919 555 0100", period="today")

The same service and method names are available from AsyncSimpleQuestBlue; only await changes.

Sending communications safely

Message and fax mutations require a named acknowledgement. Confirmations are scoped to the single call and cannot disable safety globally:

with SimpleQuestBlue() as qb:
    sent = qb.messages.send(
        from_number="+1 919 555 0100",
        to="+1 919 555 0101",
        text="Your service is ready",
        recipient_opted_in=True,
    )
    fax = qb.fax.send(
        from_number="+1 919 555 0100",
        to="+1 919 555 0101",
        file="invoice.pdf",
        destination_confirmed=True,
    )

wait_for_delivery() uses a caller-controlled attempt count and interval. It stops on QuestBlue's documented delivered or failed states and raises DeliveryTimeoutError when the bound is exhausted; it does not imply a delivery guarantee. File paths and contents are validated before any upload or send request, and enterprise fax uploads preserve their provider file IDs in the operation result.

Provisioning with explicit confirmation

Provisioning helpers accept primitive values but keep the typed request models as the final validation authority. Every billable, routing, compliance-sensitive, or destructive operation has a matching confirmation keyword. Use dry_run=True to inspect the normalized operation without calling QuestBlue:

with SimpleQuestBlue() as qb:
    plan = qb.numbers.buy(
        "+1 919 555 0100",
        trunk="main",
        dry_run=True,
    )
    print(plan.operations)

    purchased = qb.numbers.buy(
        "+1 919 555 0100",
        trunk="main",
        confirm_billable=True,
    )
    print(purchased.raw)  # original typed provider response

Inventory selection is always a separate read: numbers.search() and fax.search() never buy a number. Likewise, porting.create_draft() always sends status="draft"; the simple layer never submits an LNP request. Server, SIP trunk, Fax.Pro, enterprise-fax, international DID, backup, and number lifecycle helpers follow the same one-call confirmation and dry-run contract.

Composite workflows

Workflow builders return an inspectable plan and perform no I/O until execute() is called. A caller-supplied correlation ID and journal hook make every planned, completed, failed, or uncertain step available to application persistence:

events = []
plan = qb.workflows.voice_number(
    "+1 919 555 0100",
    "main",
    password="generated-secret",
    correlation_id="order-42",
    journal_hook=events.append,
)
print(plan.operations)
result = plan.execute(confirm_routing_change=True, confirm_billable=True)

Workflows stop at the first failure. Completed provider changes are not silently rolled back, and the result reports partial, failed, or uncertain status with recovery guidance. In particular, a timeout after a mutation is uncertain and must be reconciled with QuestBlue before retrying. Async plans expose the same builders and use await plan.execute(...); async journal hooks may also be awaitable.