Skip to main content

Program AI Automations.

Describe the job in the task editor. Review the AI builder’s draft, then run it on qbash.

company = qbash.inputs.get("company")
deal = qbash.integrations.pipedrive.search_deals(
term=company,
)["deals"][0]
news = qbash.ai.gemini_google_search(
f"{company} news since {qbash.run.last_run_at}"
)
notes = qbash.memories.search(
company,
folder="crm-notes",
limit=5,
)
brief = qbash.ai.run_prompt(
slug="account-brief",
variables={
"deal": deal, "news": news, "notes": notes,
},
)
qbash.create_chat("Account brief", brief)

Model and provider agnostic.

  • Anthropic
  • OpenAI
  • Gemini
  • TypeSafe
  • xAI
  • OpenRouter
  • Amazon
  • Perplexity
  • MoonshotAI
  • Meta
  • Qwen

Iterate with AI in the task editor.

Describe a job or a change you want to make. The builder drafts or updates the task in the editor, where you can review the result and continue refining it.

Write and test prompts in the task editor.

Prompts and playgrounds now live inside tasks. Each prompt belongs to the task that calls it, so saving the task versions the prompt with that code.

Inline prompt
The inline prompt editor with the model bar, output format toggle, and a variable in the user message

Write the prompt next to the code

Add an inline prompt from Package. It holds system and user messages, {{variables}} the script fills in, model settings, and a JSON schema when later code reads named fields. The script runs it by slug.

Test it on the unsaved draft

Test run executes the draft with its prompts as they stand in the editor. The console shows the result and the logs, and the run is flagged as a test.

Compare models on the same input

Pick another model in the prompt’s model bar and click Test run again. To see answers next to each other, call the prompt once per model with run_prompt and model= in one run, then return the answers together.

Give a prompt tools

Switch it to Agent mode and check the abilities it may use. The script passes the connectors, memory folders and linked tasks for each call. It works in turns until it has an answer, and the script can stream that answer as the run’s output.

qbash Tasks

A Task is a Python program for a repeatable workflow. Its code sets the sequence, and model calls handle the steps that need judgment.

A Task

qbash

The steps are fixed in code, and you version and test the task like an application.

An Agent

Claude · ChatGPT · qbash Projects

One model deciding as it goes. Flexible for open-ended work a person steers, but non-deterministic, and can burn a lot of tokens.

A Workflow Builder

Zapier · n8n

A visual canvas wiring fixed steps together. Deterministic, but the canvas caps how much logic it can express, and every change means re-wiring it.

The SDK

qbash.ai

Prompts belong to the task that uses them. Choose a model for each step, use Jev for typed classification and routing, and keep structured outputs, file and image inputs, and web search in the same task.

# Run a prompt owned by this task
summary = qbash.ai.run_prompt(
    slug="weekly-summary", variables={"commits": commits},
)

# Classify with Jev
questions = {"team": {
    "type": "choice",
    "instructions": "Route this ticket",
    "criteria": {
        "billing": "Payments",
        "technical": "Product issues",
    },
}}
route = qbash.ai.raw(
    ticket,
    model="jev-latest",
    output_schema=questions,
)

qbash.integrations

Read from and write to a connected service in one call. Real API calls made server-side, not routed through a model. They don't burn tokens: typed, direct, and identical every run.

invoices = qbash.integrations.xero.list_invoices(status="AUTHORISED")["data"]["Invoices"]
qbash.integrations.slack.create_message(channel="#finance", text=summary)

qbash.files

Read, write, and convert doc, xlsx, and pptx. Split and recombine PDFs, fill a PowerPoint template, zip outputs, and get a presigned download URL back.

deck = qbash.powerpoint.fill(template_bytes=template, data=values)
url = qbash.files.download_url(qbash.files.write(deck, "qbr.pptx"))

qbash.memories

Generate a RAG database from content (PDFs, docs, and more) on the fly, or store markdown files and JSON objects between agentic runs. No vector DB or embedding pipeline to stand up.

qbash.memories.store("reconciliation-log", content=summary)
hits = qbash.memories.vector_search("supplier terms", limit=5)

The rest of the SDK

Eight more modules. Open one for what it does and a sample call.

Provides a task-scoped key-value store with get, set, and a required TTL. Holds an expensive result, such as an API pull or a model call, so a later run reuses it until it expires.

rate = qbash.cache.get_or_set(
    "fx-usd-nzd",
    lambda: fetch_rate("USD", "NZD"),
    ttl="1h",
)
qbash.cache.set("last-cursor", cursor, ttl="1d")

Searches and reads the chat threads of the person running the task, and only that person's threads. Shows a consent banner on the run form. Turns prior conversations into task input.

threads = qbash.chats.search("supplier dispute", limit=10)["threads"]
messages = qbash.chats.read(threads[0]["id"], limit=50)

Records that a unit of work finished, under a key you choose. On retry, qbash.checkpoint.done() reports which keys were already marked, so work you guard runs once and its model calls and side effects are not repeated. A Slack post inside that guard does not post twice.

for invoice in invoices:
    if qbash.checkpoint.done(f"invoice-{invoice['id']}"):
        continue
    reconcile(invoice)
    qbash.checkpoint.mark(f"invoice-{invoice['id']}")

Reaches any HTTP API through a protected request helper, so an internal or niche service with no prebuilt connector still works from the same script. Returns status, headers, and body for the next step to use.

res = qbash.http.request(
    "GET",
    "https://api.vertical-tool.example/v2/orders",
    headers={"Authorization": f"Bearer {token}"},
)
orders = res["body"]["data"]

Reads run inputs with qbash.inputs.get, merging the trigger payload with values a person entered. You declare each input with a type, including text, number, date, select, file, and credential, and qbash renders those declarations as a run form, so a teammate reruns the task by filling fields. A credential input lets the runner pick which account the task acts on.

period = qbash.inputs.get("period", default="last-month")
dry_run = qbash.inputs.get("dry_run", default=False)

Fans a function out over a list and collects the results. Takes concurrency, a per-minute rate limit, retries, and an optional checkpoint name as arguments, so fan-out over hundreds of items stays a single call. Pass checkpoint= and each completed item is recorded and skipped on retry.

results = qbash.parallel(
    fetch_statement,
    accounts,
    concurrency=5,
    checkpoint="statements",
    continue_on_error=True,
)

Fills {{placeholders}} in a PPTX file and preserves the original formatting, so a recurring deck generates from the template the team already approved. Placeholder keys are word characters only.

deck = qbash.powerpoint.fill(
    template_bytes=qbash.files.read(template, encoding="bytes"),
    data={"client": client, "arr": arr},
)

Exposes the execution id for correlating against logs, the task author, the person who pressed Run, the company and project, timestamps, and a test-run flag. qbash.run.last_run_at returns the previous successful run’s time, and None on the first run, so "process everything since last run" is one property.

since = qbash.run.last_run_at        # None on the first run
if qbash.run.is_test:
    accounts = accounts[:1]

Managed deployment and runtime.

  • Auto-deploys on save

    Press save and the new version is live. No deploy step, no servers.

  • Revisions and rollback

    A revision on every save. Follow each change and roll back in a click.

  • Durable runs

    Retries, checkpoints that resume a long flow, and timeouts.

  • Logged and audited

    Every run records status, inputs, output, errors, and what triggered it.

  • Easy model upgrades

    Swap or upgrade the model behind any step with an edit. No rewrite, no redeploy.

  • Almost no upkeep

    No runtime to patch, no dependencies to drift, no servers to keep alive.

Python LLM hosting for production workflows →

Flexible distribution.

Start a task however you like - and send the result to wherever your users are

In qbash

Run it from Chat or a Project, and the typed inputs render as a form anyone can fill.

Chat Providers

Claude, ChatGPT, Claude Code, and Cursor. Over MCP, a task runs like one of their own tools.

Build a Python MCP server →

Triggers & HTTP

POST inputs as JSON, or start it from a webhook, an inbound email, or a connected-service event.

Publish a Python AI API endpoint →

Schedule

Put it on a cron and it runs unattended, every night or every few minutes.

A task is an application.

The AI flows you build today resemble complex applications: they require logic, control flow, state, maintenance and have dependencies.

It’s code

Loops, branches, and functions let your code set what runs and when. Tasks are reviewable, versioned, and diffable, so a team can maintain them like software.

for ticket in tickets:route(ticket, triage(ticket))

Model calls and decisions

Choose a model for each task-owned prompt without changing the surrounding Python. Use Jev for typed classification, scoring, and routing in the same task, with each model used for the work it is built to do.

run_prompt("draft-reply", {"ticket": ticket})raw(ticket, model="jev-latest", ...)

Memory built in

Tasks can use memories between runs, along with caching, checkpoints, and vector search for documents.

vector_search("supplier terms")5 hits · 0.31s

Control model calls

Call a model when the task reaches that step. Cache results and checkpoint long runs to avoid repeating work.

qbash.cache.get_or_set("fx-rate", fetch)cached · no extra model call

A test harness

A tests.py file can live with the task. Test the flow end to end before it runs against real services.

tests.py 14 passed0 calls left the sandbox

A sandboxed runtime

Every run is isolated, with restricted builtins and a curated standard library. Credentials are brokered outside your code.

import osImportError: not allowed in Tasks

Task vs Agents

A task orchestrates a deterministic process from code: you decide what runs and in what order, so it is reliable, you can trust it, and it costs what you expect. An agent decides its own path, which is powerful for open-ended work a person steers, but non-deterministic.

Written as code
# the code you always could have written
keys = qbash.integrations.jira.get_tickets_by_time_span(
    start_date=qbash.inputs.get("since"), end_date=str(qbash.dates.today()),
)["tickets"]

for key in keys:
    issue = qbash.integrations.jira.get_ticket(ticket_id=key)["ticket"]
    # the line you couldn't, until now: which of 12 teams is this?
    triage = qbash.ai.run_prompt(
        slug="triage-issue", variables={"issue": issue},
    )                                     # -> {"team": ..., "priority": ...}

    qbash.integrations.jira.update_ticket(
        ticket_id=key, field_id="labels", tag_name=triage["team"],
    )
    qbash.integrations.jira.update_ticket(
        ticket_id=key, field_id="priority", tag_name=triage["priority"],
    )
Written as a prompt

Go through our open Jira issues that have no team assigned. For each one, read it, decide which of our twelve teams should own it and how urgent it is, then update the issue. Keep going until everything is triaged.

Oh, and if an issue looks like a duplicate, link it to the original instead of assigning a team. If you cannot tell which team owns it, drop it in the triage backlog with a short note on why. Skip anything already assigned, do not touch closed issues, and if something looks like an outage, mark it urgent and post a heads-up in the incidents channel.

Also in the box.

Named here so you don't find out from the docs.

Direct Postgres and SQL Server clients
Read what your pipelines already built, transactions and parameterized queries included.
Resumable SFTP and FTP transfers
A multi-GB partner drop survives a retry instead of restarting.
qbash.parsers
Chunked csv, xlsx, and line reads, so a large file streams through instead of loading whole.
qbash.dates
UTC-correct dates, rather than whatever local clock the container happened to have.
Typed exceptions
Catch a blocked credential, a schema parse failure, or a cancelled run as distinct errors.
A live console in the editor
Try any single call against a real credential before writing the loop around it.
Pre-save validation
A broken reference blocks the save in the editor, not the 3am run.
Named credentials, picked per call or per run
One script serves every client account instead of fifteen drifting copies.
Credentials are org objects
Who may use a connection is decided once and enforced everywhere it is referenced.
Sharing, permissions, and run history
View and edit levels per person or org, and a record of who ran what. The task survives its author.
The assistant works from a diff of your hand-edits
It sees what you changed between its turns, so there are no silent overwrites.
Tasks as MCP tools
Start a task from Claude Code, Cursor, or another connected MCP client, then check its status and logs.

Easier to maintain than a node graph.

A canvas is hard to manage. Changes are onerous and tough to follow. A task moves the problem into code, where logic is easier to reason about, faster to modify, and version tracking is fast and simple.

account-brief.py
# Find every open deal at this company.
company = qbash.inputs.get("company")
deals = qbash.integrations.pipedrive.search_deals(
    term=company,
)["deals"]

# For each deal, grab news since the last run.
research = []
for deal in deals:
    news = qbash.ai.gemini_google_search(
        f"{company} news since {qbash.run.last_run_at}"
    )
    if news:
        research.append({"deal": deal, "news": news})

# Use a prompt owned by this task to write the brief.
brief = qbash.ai.run_prompt(
    slug="account-brief",
    variables={"research": research},
)

qbash.integrations.slack.create_message(
    channel="#revenue",
    text=brief,
)
account-brief.workflow
The account-brief workflow on a visual canvas: chat trigger, search deals, a loop that summarizes and posts, plus template, search, and condition branches.

AI workflow automation where the workflow is code.

An AI workflow is a chain of model calls with business rules between them. In a task, Python holds the chain: each step can call a task-owned prompt, and your code reads the answer and decides what runs next.

Branch on what the model returned

A classification picks which prompt runs next, and a low-confidence answer goes to a person instead of the next step.

Admin model controls

An admin sets which models the organization can use, so expensive models stay with the teams that need them.

The qbash toolkit.

A toolkit for building applications and connecting people, systems and agents.

AnthropicOpenAIGoogle

Tasks

Build and run Python applications with code, AI and connected tools.

GmailSlackJiraAirtablePipedriveGitHub

Connectors

Authenticate once. Give applications and agents controlled access to connected services.

MCP servers

Build a hosted server from your chosen Tasks, connector tools and memory folders.

Memories

Store documents and findings that Tasks and agents can search, read and update.

Ship your first system.

Open the editor, write a script against your connected accounts, and put it on a trigger. Or describe it, and the builder drafts the task as code you edit. Starter and Pro both begin with a 14-day free trial.

Get started