# pyquickbase API reference: App This describes version 1.0.0rc13. Other pages: [Quickbase](https://matthew-williams-solutions.github.io/pyquickbase/reference/quickbase.txt), [Table](https://matthew-williams-solutions.github.io/pyquickbase/reference/table.txt), [Report](https://matthew-williams-solutions.github.io/pyquickbase/reference/report.txt), [Operation](https://matthew-williams-solutions.github.io/pyquickbase/reference/operation.txt), [SchemaOperation](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-operation.txt), [Queries](https://matthew-williams-solutions.github.io/pyquickbase/reference/queries.txt), [Records and results](https://matthew-williams-solutions.github.io/pyquickbase/reference/records.txt), [Metadata](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata.txt), [Schema changes](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes.txt), [Policies](https://matthew-williams-solutions.github.io/pyquickbase/reference/policies.txt), [Rules](https://matthew-williams-solutions.github.io/pyquickbase/reference/rules.txt), [Retries](https://matthew-williams-solutions.github.io/pyquickbase/reference/retries.txt), [Tools for models](https://matthew-williams-solutions.github.io/pyquickbase/reference/tools.txt), [Errors](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors.txt). An [`App`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/) lists an app's details and gives you its tables. Get one from [`Quickbase.app()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/quickbase/#app). Getting one doesn't contact Quickbase, so an ID that doesn't exist only fails on first use. The examples below use `app`, made as shown here, in an app that has a table named `Job Tasks` with three records. A live test runs every example with both clients and checks its output. [`AsyncApp`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/) has the same methods, which you `await`, but `table()`, `url()`, and `settings_url()` are called without it. Sync: ```python from pyquickbase import Quickbase with Quickbase( realm="acme", token="your_user_token" ) as qb: app = qb.app("bq1234567") ``` Async: ```python from pyquickbase import AsyncQuickbase async with AsyncQuickbase( realm="acme", token="your_user_token" ) as qb: app = qb.app("bq1234567") ``` ## What the methods return | Type | What it is | Returned by | |---|---|---| | [`App`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/) | A Quickbase app. Create one with `qb.app(app_id)`. This makes no network calls. | [`copy()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#copy) | | [`AppEvent`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppEvent) | Something in an app that runs when records change or on a schedule, such as a webhook. | [`events()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#events) | | [`AppInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppInfo) | An app's metadata. | [`info()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#info) | | [`Role`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#Role) | A role in an app. | [`roles()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#roles) | | [`SchemaChangeResult`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaChangeResult) | What applying a plan did. | [`apply_schema_changes()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#apply_schema_changes) | | [`SchemaOperation`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-operation/) | One schema operation, for a model's tool call. Create one with `app.operation()`. | [`operation()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#operation) | | [`SchemaPlan`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaPlan) | What a list of changes would do to an app, checked against it as it is now. | [`preview_schema_changes()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#preview_schema_changes) | | [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) | A table in an app. Create one with `app.table("Tasks")`. | [`table()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#table), [`ensure_table()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#ensure_table), [`create_table()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#create_table) | | [`TableInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#TableInfo) | A table's metadata. | [`tables()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#tables) | ## Look up the app's details For the full picture, see [Reading records](https://matthew-williams-solutions.github.io/pyquickbase/guides/reading/). ### `App.id` The app's ID, as passed to `qb.app()`. ```python id: str ``` Sync example: ```python print(app.id) ``` Async example: ```python print(app.id) ``` Output: ```text bq1234567 ``` ### `App.info()` Get the app's metadata, from the client's cache if it's there. ```python info() -> AppInfo ``` **Returns** [`AppInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppInfo) An [`AppInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppInfo), with the app's `id`, `name`, `description`, `time_zone`, and `date_format`. **Raises** - [`AuthenticationError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#AuthenticationError): Quickbase doesn't recognize the app ID. It answers with a 401 here, rather than a 404. - [`PermissionDeniedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PermissionDeniedError): The token's role can't edit the app's structure. Sync example: ```python info = app.info() print(info.id) ``` Async example: ```python info = await app.info() print(info.id) ``` Output: ```text bq1234567 ``` ### `App.tables()` List the app's tables, from the cache unless `refresh` is set. ```python tables(*, refresh: bool = False) -> list[TableInfo] ``` **Parameters** - **`refresh`** `bool`, default `False` Ask Quickbase instead of using the client's cache, which keeps metadata for five minutes by default. A change made through this client updates the cache, so this is for changes made elsewhere, such as in the Quickbase interface. **Returns** `list[` [`TableInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#TableInfo) `]` A [`TableInfo`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#TableInfo) for each table the token can see. **Raises** - [`AuthenticationError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#AuthenticationError): Quickbase doesn't recognize the app ID. It answers with a 401 here, rather than a 404. Sync example: ```python tables = app.tables() print(sorted(t.name for t in tables if "Job" in t.name)) ``` Async example: ```python tables = await app.tables() print(sorted(t.name for t in tables if "Job" in t.name)) ``` Output: ```text ['Job Projects', 'Job Tasks'] ``` ### `App.roles()` List the app's roles, from the cache unless `refresh` is set. Quickbase has no API for reading or changing what a role can do. ```python roles(*, refresh: bool = False) -> list[Role] ``` **Parameters** - **`refresh`** `bool`, default `False` Ask Quickbase instead of using the client's cache, which keeps metadata for five minutes by default. A change made through this client updates the cache, so this is for changes made elsewhere, such as in the Quickbase interface. **Returns** `list[` [`Role`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#Role) `]` A [`Role`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#Role) for each role in the app. **Raises** - [`PermissionDeniedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PermissionDeniedError): The token isn't an administrator's. Sync example: ```python roles = app.roles() print(any(role.name == "Administrator" for role in roles)) ``` Async example: ```python roles = await app.roles() print(any(role.name == "Administrator" for role in roles)) ``` Output: ```text True ``` ### `App.events()` List the app's webhooks, Quickbase actions, notifications, subscriptions, reminders, and the automations that records trigger. Quickbase leaves out scheduled automations, and doesn't say which changes set each one off. With a policy, only events on tables it shows are listed, without their owners. ```python events() -> list[AppEvent] ``` **Returns** `list[` [`AppEvent`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppEvent) `]` An [`AppEvent`](https://matthew-williams-solutions.github.io/pyquickbase/reference/metadata/#AppEvent) for each, active or not. **Raises** - [`PermissionDeniedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PermissionDeniedError): The token isn't an administrator's. Sync example: ```python events = app.events() print([event.type for event in events if event.active]) ``` Async example: ```python events = await app.events() print([event.type for event in events if event.active]) ``` Output: ```text [] ``` ## Get a table For the full picture, see [Reading records](https://matthew-williams-solutions.github.io/pyquickbase/guides/reading/). ### `App.table()` Get a table in this app by name or ID. It's checked when first used. This doesn't contact Quickbase, so a table that doesn't exist raises [`UnknownTableError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#UnknownTableError) on first use. ```python table(name_or_id: str) -> Table ``` **Parameters** - **`name_or_id`** `str`, required The table's name, or its ID. **Returns** [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) A [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/). Sync example: ```python tasks = app.table("Job Tasks") print(tasks.count()) ``` Async example: ```python tasks = app.table("Job Tasks") print(await tasks.count()) ``` Output: ```text 3 ``` ## Link to the Quickbase interface For the full picture, see [Using pyquickbase from an AI agent](https://matthew-williams-solutions.github.io/pyquickbase/agents/). ### `App.url()` Link to the app's home page in Quickbase. This makes no network calls. ```python url() -> str ``` **Returns** `str` The URL, on the client's realm. Sync example: ```python print(app.url()) ``` Async example: ```python print(app.url()) ``` Output: ```text https://acme.quickbase.com/nav/app/bq1234567/action/appoverview ``` ### `App.settings_url()` Link to the app's settings in Quickbase. This makes no network calls. ```python settings_url() -> str ``` **Returns** `str` The URL, on the client's realm. Sync example: ```python print(app.settings_url()) ``` Async example: ```python print(app.settings_url()) ``` Output: ```text https://acme.quickbase.com/nav/app/bq1234567/action/appsettingshome ``` ## Create tables For the full picture, see [Changing the schema](https://matthew-williams-solutions.github.io/pyquickbase/guides/schema/). ### `App.ensure_table()` Return the table with this name, creating it if it doesn't exist. This makes a setup script safe to run again. An existing table isn't changed. ```python ensure_table( name: str, *, description: str | None = None, single_record_name: str | None = None, plural_record_name: str | None = None, ) -> Table ``` **Parameters** - **`name`** `str`, required The table's name, ignoring case when looking for an existing table. - **`description`** `str | None`, optional The new table's description. - **`single_record_name`** `str | None`, optional What the new table calls one record, such as `Project`. - **`plural_record_name`** `str | None`, optional What the new table calls several records, such as `Projects`. **Returns** [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) A [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) for the existing or new table. **Raises** - [`AuthenticationError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#AuthenticationError): Quickbase doesn't recognize the app ID, or the token can't reach it. - [`BadRequestError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#BadRequestError): Quickbase refused to create the table. - [`PolicyRefusedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PolicyRefusedError): The client's policy doesn't allow creating a table. Nothing was created. Sync example: ```python sites = app.ensure_table("Job Sites") again = app.ensure_table("Job Sites") first_info = sites.info() again_info = again.info() print(first_info.id == again_info.id) ``` Async example: ```python sites = await app.ensure_table("Job Sites") again = await app.ensure_table("Job Sites") first_info = await sites.info() again_info = await again.info() print(first_info.id == again_info.id) ``` Output: ```text True ``` ### `App.create_table()` Create a table and return it. ```python create_table( name: str, *, description: str | None = None, single_record_name: str | None = None, plural_record_name: str | None = None, ) -> Table ``` **Parameters** - **`name`** `str`, required The table's name. - **`description`** `str | None`, optional The table's description. - **`single_record_name`** `str | None`, optional What the table calls one record, such as `Project`. - **`plural_record_name`** `str | None`, optional What the table calls several records, such as `Projects`. **Returns** [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) A [`Table`](https://matthew-williams-solutions.github.io/pyquickbase/reference/table/) for the new table. **Raises** - [`BadRequestError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#BadRequestError): The app already has a table with this name. Sync example: ```python sites = app.create_table( "Job Sites", description="Where each job takes place.", single_record_name="Site", plural_record_name="Sites", ) info = sites.info() print(info.name) ``` Async example: ```python sites = await app.create_table( "Job Sites", description="Where each job takes place.", single_record_name="Site", plural_record_name="Sites", ) info = await sites.info() print(info.name) ``` Output: ```text Job Sites ``` ## Change the schema with a plan For the full picture, see [Changing the schema](https://matthew-williams-solutions.github.io/pyquickbase/guides/schema/). ### `App.preview_schema_changes()` Work out what schema changes would do, without making them. Each change is compared with the app as it is now, in order, so a later one can use a table or field an earlier one makes. The plan says what each would set, what else it would do or affect, such as records that lose a deleted field's values, and anything that stops it, including the client's own limits. A change the app already has counts as done, so planning again after a failed apply shows only what's left. This only reads. It may count records, and look for duplicates before making a field unique, reading up to 10,000 records. ```python preview_schema_changes( changes: Sequence[SchemaChange], ) -> SchemaPlan ``` **Parameters** - **`changes`** `Sequence[SchemaChange]`, required The changes, such as `CreateField(table="Tasks", label="Due", type="date")`. **Returns** [`SchemaPlan`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaPlan) A [`SchemaPlan`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaPlan), for [`apply_schema_changes()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#apply_schema_changes). **Raises** - [`InvalidValueError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#InvalidValueError): `changes` is empty. Nothing was read. Sync example: ```python from pyquickbase import CreateField change = CreateField( table="Job Tasks", label="Crew", type="text" ) plan = app.preview_schema_changes([change]) for planned in plan.changes: print(planned.status, planned.summary) ``` Async example: ```python from pyquickbase import CreateField change = CreateField( table="Job Tasks", label="Crew", type="text" ) plan = await app.preview_schema_changes([change]) for planned in plan.changes: print(planned.status, planned.summary) ``` Output: ```text ready Add field "Crew" (text) to Job Tasks. ``` ### `App.apply_schema_changes()` Make the changes in a plan, in order, if the app hasn't changed since it was made. The changes are planned again first, and nothing is sent unless that finds the same schema and nothing stopping them. Quickbase has no way to undo a change, so if one fails, the ones before it stay made, and the ones after it aren't attempted. ```python apply_schema_changes( plan: SchemaPlan, ) -> SchemaChangeResult ``` **Parameters** - **`plan`** `SchemaPlan`, required From [`preview_schema_changes()`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/#preview_schema_changes). **Returns** [`SchemaChangeResult`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaChangeResult) A [`SchemaChangeResult`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-changes/#SchemaChangeResult), with what was made and what was already there. **Raises** - [`SchemaMismatchError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#SchemaMismatchError): The schema has changed since the plan was made. Nothing was sent. - [`InvalidValueError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#InvalidValueError): The plan has blockers, or is for another app. Nothing was sent. - [`SchemaChangeError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#SchemaChangeError): A change failed. Its `result` says which were made. Sync example: ```python from pyquickbase import CreateField change = CreateField( table="Job Tasks", label="Crew", type="text" ) plan = app.preview_schema_changes([change]) result = app.apply_schema_changes(plan) print(len(result.applied)) again = app.preview_schema_changes([change]) print(again.changes[0].status) ``` Async example: ```python from pyquickbase import CreateField change = CreateField( table="Job Tasks", label="Crew", type="text" ) plan = await app.preview_schema_changes([change]) result = await app.apply_schema_changes(plan) print(len(result.applied)) again = await app.preview_schema_changes([change]) print(again.changes[0].status) ``` Output: ```text 1 already_done ``` ## Give a model the schema For the full picture, see [Using pyquickbase from an AI agent](https://matthew-williams-solutions.github.io/pyquickbase/agents/). ### `App.operation()` Make a schema operation into a tool for a model, with its JSON Schema. `describe_schema` lists the tables and fields the client can read. `preview_schema_changes` says what a list of changes would do, without making them, and `apply_schema_changes` makes them, but only with the `confirmation` that `preview_schema_changes` returned for exactly those changes, within 15 minutes, while the schema is as it was then. Show the person each plan before they approve it. The schema offers only the kinds of change, tables, and field types the client's policy allows, and a plan reports anything it would refuse. Give the model a client with a policy, so that what it can change and see is limited, and a formula, lookup, or summary it adds can only show what it could already read. ```python plan = app.operation( "preview_schema_changes" ) tool = { "name": "plan_changes", "input_schema": plan.schema, } result = plan.run(arguments) ``` ```python operation( name: SchemaOperationName, *, max_changes: int = 20 ) -> SchemaOperation ``` **Parameters** - **`name`** `SchemaOperationName`, required `describe_schema`, `preview_schema_changes`, or `apply_schema_changes`. - **`max_changes`** `int`, default `20` The most changes one plan may hold. **Returns** [`SchemaOperation`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-operation/) A [`SchemaOperation`](https://matthew-williams-solutions.github.io/pyquickbase/reference/schema-operation/), with its `schema` and `run()`. **Raises** - [`ConfigurationError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#ConfigurationError): `name` isn't a schema operation, `max_changes` isn't a positive whole number, or the client's policy allows no schema change for `preview_schema_changes` or `apply_schema_changes`. Sync example: ```python plan = app.operation( "preview_schema_changes", max_changes=5 ) print(plan.schema["properties"]["changes"]["maxItems"]) ``` Async example: ```python plan = await app.operation( "preview_schema_changes", max_changes=5 ) print(plan.schema["properties"]["changes"]["maxItems"]) ``` Output: ```text 5 ``` ## Copy or delete the app For the full picture, see [Working with apps](https://matthew-williams-solutions.github.io/pyquickbase/guides/apps/). ### `App.copy()` Copy this app, with the same tables, fields, reports, and forms, and return the copy. ```python copy( name: str, *, description: str | None = None, keep_data: bool = False, keep_files: bool = False, keep_users: bool = False, assign_token: bool = True, ) -> App ``` **Parameters** - **`name`** `str`, required The copy's name. Quickbase allows several apps with the same name. - **`description`** `str | None`, optional The copy's description. - **`keep_data`** `bool`, default `False` Copy the records too. - **`keep_files`** `bool`, default `False` With `keep_data`, copy file attachments too. - **`keep_users`** `bool`, default `False` Give the copy's users the roles they have here. Otherwise the roles are copied without anyone in them. - **`assign_token`** `bool`, default `True` Let this client's token reach the copy, which a token limited to some apps can't otherwise do. **Returns** [`App`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/) An [`App`](https://matthew-williams-solutions.github.io/pyquickbase/reference/app/) for the copy. **Raises** - [`InvalidValueError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#InvalidValueError): `name` is empty. Nothing was sent. - [`PermissionDeniedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PermissionDeniedError): The token's user can't copy this app. - [`PolicyRefusedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PolicyRefusedError): The client has a policy, which covers one app. Nothing was sent. Sync example: ```python staging = app.copy("Jobs (staging)") print(staging.id != app.id) ``` Async example: ```python staging = await app.copy("Jobs (staging)") print(staging.id != app.id) ``` Output: ```text True ``` ### `App.drop()` Delete this app, with all its tables and records. Quickbase can't undo this. ```python drop(*, confirm: str) -> None ``` **Parameters** - **`confirm`** `str`, required The app's exact name, so a wrong variable can't delete the wrong app. **Raises** - [`InvalidValueError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#InvalidValueError): `confirm` isn't the app's exact name. Nothing was sent. - [`PermissionDeniedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PermissionDeniedError): The token's user can't delete this app. - [`PolicyRefusedError`](https://matthew-williams-solutions.github.io/pyquickbase/reference/errors/#PolicyRefusedError): The client has a policy, which covers one app. Nothing was sent. Sync example: ```python staging = app.copy("Jobs (staging)") staging.drop(confirm="Jobs (staging)") ``` Async example: ```python staging = await app.copy("Jobs (staging)") await staging.drop(confirm="Jobs (staging)") ```