Cheqq Docs
Core ConceptsApps

Audience and bindings

How audience tiers and data/logic bindings combine into a default-deny permission model for Apps.

An App's access to data is governed by two independent controls that both have to allow a request: the audience tier a visitor falls into, and the bindings that expose specific collections, fields, and workflows. Both default to denying access.

Audience tiers structurally limit what's possible

Audience isn't just a login gate — it also caps what operations are ever allowed, regardless of how a binding is configured:

  • Just me — only you, the author, can open the App at all.
  • Team — any workspace member can open it, with whatever operations their binding allows.
  • Invited — people you've specifically invited sign in with their Cheqq account through a branded auth screen; visitors get an owner, member, or guest role depending on how they were invited.
  • Public — anyone can open it without signing in. Update and delete operations are never allowed on a Public App, no matter what a data binding's access list says — public visitors are structurally capped at read (and insert, if explicitly bound).

Data bindings

A data binding connects one collection to the App and defines exactly what's reachable:

  • Operations — an allow-list of read, insert, update, delete. Only the operations listed are permitted (subject to the audience cap above).
  • Fields — an explicit allow-list of field names. This is not a hide-list — it's the complete set of fields the App can ever see or write. A binding has to name at least one: an empty list is refused when you configure it, since an App bound that way could reach nothing and would fail the first time a visitor used it.
  • Fields a visitor may change (optional) — which of those fields the App may write. The rest stay read-only: the App can show them but never set them. Without this list, every field the App can see can also be written.
  • A server-enforced filter (optional) — restricts which rows the binding can touch (e.g. only records belonging to the visitor). This filter can't be widened by anything the App's frontend sends; it's applied on the server for every request.

Example

Binding a Contacts collection with fields: ["name", "company"] and access: ["read"] lets the App display a contact's name and company — the contact's email, phone, and every other field stay invisible, even though the collection itself has them.

If a request doesn't match any of these, it's denied with a specific reason (useful if you're debugging an integration against the App's public API): no binding exists for that collection, the operation isn't in the binding's allow-list, the audience forbids the operation, no fields are exposed at all, or the specific field requested isn't in the allow-list.

Reading through a binding

A read binding supports three shapes, all of them inside the binding:

  • List the bound collection.
  • Fetch one record by id, so rendering a single page doesn't mean paging the whole collection to find it.
  • Narrow a list with ?filter.<field>=<value>.

Every record carries an updatedAt, so a site built on the API has something to hang a lastmod or dateModified on.

A caller's filter can only narrow

Filtering never widens what a binding exposes. Filtering on a field the binding doesn't expose is rejected outright; the binding's own filter always merges last, so no caller value can displace it; and a filter that fights the binding (asking for drafts against a published-only binding) returns an empty page rather than the binding's rows.

Fetching by id re-checks the binding's filter against the row it loaded, and a row that fails it is reported as missing, not as forbidden — otherwise the id space becomes a way to probe for records the App is meant to hide.

Records that belong to the visitor

A binding can hold a collection to the signed-in visitor: only the records whose email field matches the email of the visitor's Cheqq account. This is how a portal shows each client only their own projects, or each resident only their own lease.

  • Reads return only that visitor's records.
  • A record they add is stamped with their email, whatever the request says, and they can't change it afterwards.
  • Updates and deletes reach only their own records.
  • A request with no signed-in visitor is refused, so this only works on an App people sign in to, never a Public one.

A binding can also let through only records in certain states, such as invoices that are Sent, Paid or Overdue. Combined with the visitor match, a client sees their invoices once they go out, and a draft stays yours even with their email already on it. A state added later stays hidden until you add it to the list.

A visitor's changes stay inside the binding too. A record they add gets the fixed values filled in for them, a field limited to a list of values must be one of the list, and an edit can't move a record out of what the binding shows: change it to a value the binding doesn't allow and it's refused, with the record unchanged.

Limits on what visitors send

A Public or Invited App accepts up to 500 new records a day. Past that, it's told "This app has taken all the new submissions it can for today. Please try again tomorrow." Every field has a length limit, from 320 characters for an email to 20,000 for long text, and one submission can't be larger than 1 MB.

Wider access needs your yes

A change that would let an App reach more than it does now, such as dropping the visitor match above or opening the App to more people, waits for a person to approve it, even in Auto mode. See Cheqq asks before an App reaches more.

Bindings in the App's code

The App's code reads and writes your data through a module generated from its bindings on every build. A field or operation that isn't bound doesn't exist there, so code that reaches for one fails the build — the App Builder hears about it before a visitor ever could.

Logic bindings

A logic binding connects a workflow's webhook trigger to the App, so the App's frontend can invoke that workflow directly — this is the only way an App can trigger a Wapp workflow. There's no equivalent for other trigger types (schedule, email, collection change); those keep firing independently of the App.

Continue with Branded auth and domains for how the sign-in screen works.

Was this page helpful?