All integrations

Bubble

DEVELOPER · DEVELOPER

Create, read, change, and delete the things in their own Bubble app’s database.

Connect Bubble with Fabriq Developer

Your user pastes their Bubble credential on a page Agentic Fabriq hosts, so it never reaches your backend and never becomes one shared key across all of your users. Same call as OAuth, one more argument.

from af_sdk import AgenticFabriq

af = AgenticFabriq()                      # reads AF_APP_ID / AF_APP_SECRET

url = await af.initiate_connection(request, provider="bubble_io", method="api_key")
return redirect(url)                      # an AF-hosted page, not yours

async with await af.for_user(request) as session:
    tools = await session.get_tools()     # only what this user granted

Provider names are surfaces, not vendors, and the authoritative list is the capabilities endpoint rather than any page including this one — an unknown name returns 400 with the valid list. Fabriq Developer is free to start and needs no provider paperwork. Start free or read the quickstart.

Connecting Bubble in practice

Bubble’s Data API is off until the app owner ticks "This app exposes a Data API" in Settings - API and then exposes each data type one at a time, and after that the privacy rules plus separate "Create via API", "Modify via API" and "Delete via API" switches decide what a token can touch — so a technically valid token can see an app that looks empty.

Scopes that draw scrutiny

  • Admin authentication (API token) Bubble defines access by authentication mode rather than scopes. An admin API token bypasses the privacy rules that constrain user-authenticated calls, so it is the credential that sees everything.
  • Create via API / Modify via API / Delete via API These are per-data-type privacy-rule options, not scope tokens. Write access is granted by ticking them on the data type, independently of whether the same rule allows reading it.

Rate limits

  • A GET request is limited to 50,000 items; "For the Enterprise plan the limit is 10,000,000 items".
  • Pagination uses cursor and limit, and responses report cursor (the rank of the first item), count (items in this response) and remaining (records left).

Who has to approve

The app’s own owner is the gate rather than a tenant admin: "The Data API is disabled by default. To enable it, go to Settings - API and check the box ‘This app exposes a Data API’", and then each data type is exposed individually. Nothing an agent does can turn it on.

Worth knowing

  • Read access and API write access are separate decisions on the same data type, so an agent can list records it cannot modify and get no scope-shaped error explaining why.
  • Because privacy rules filter records and fields rather than rejecting the call, a misconfigured rule produces a successful, thinner response — which is much harder to notice than a 403.
  • The 50,000-item ceiling is per GET request, not per page, so a bulk export of a large table has to be split by filter as well as by cursor.
  • Exposure is per data type, so adding a new type to the app does not add it to the API and the integration quietly stops covering new data.

Checked against The Data API (2026-09-29), Data API requests (2026-09-29)

Acts as the person, not as itself

Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.

Credentials never touch the agent

Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.

Every call on the record

Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.

What an agent can do

Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.

bubble_io_delete_obj_by_typename_by_uidWRITE

Delete a thing, via DELETE /api/1.1/obj/{typename}/{uid}. PERMANENT: Bubble's Data API offers no recycle bin and no undo, and the admin token disregards privacy rules, so nothing in the app stops this. Other things that LINK to the deleted one keep a reference that no longer resolves, which is worth checking before deleting a type other types point at. It answers 204 with an EMPTY body, so this reply says nothing about what happened -- confirm by asking for the thing again (404 afterwards) or by listing the type and checking the id is gone.

api
bubble_io_get_obj_by_typenameREAD

List things of a type, via GET /api/1.1/obj/{typename}. THIS IS THE LIST AND SEARCH TOOL and it is the only one: Bubble's Data API has no separate search endpoint, so filtering, sorting and paging all happen here. The reply is `{"response": {"results": [...], "cursor", "count", "remaining"}}` -- `count` is how many came back, `remaining` how many are left after them, so page by adding `count` to `cursor` until `remaining` is 0. Bubble returns at most 100 things per call. `constraints` is a STRINGIFIED JSON ARRAY of `{key, constraint_type, value}` objects, ANDed together -- for example `[{"key":"status","constraint_type":"equals","value":"open"}]`. The connection's admin token disregards privacy rules, so this returns every thing of the type rather than what any particular user would see, and every field of it. A type that is not exposed under Settings -> API answers `404 Type not found`.

api
bubble_io_get_obj_by_typename_by_uidREAD

Get a thing by its unique id, via GET /api/1.1/obj/{typename}/{uid}. The reply is `{"response": {...}}` carrying `_id`, `Created By`, `Created Date`, `Modified Date` and every data field of that thing. Use 'List things of a type' with a constraint when you have a field value rather than an id -- this route takes Bubble's `_id` and nothing else. An id that does not exist, or a type that is not exposed under Settings -> API, answers 404 with a body naming which; the admin token disregards privacy rules, so a 404 here is about existence rather than permission.

api
bubble_io_patch_obj_by_typename_by_uidWRITE

Change some fields on a thing, via PATCH /api/1.1/obj/{typename}/{uid}. THIS IS THE UPDATE TOOL -- use it rather than 'Replace every field on a thing', which empties everything you do not send. The body is a flat object of field name to value, and the keys should match the spelling a GET returns. Answers 204 with an EMPTY body, so this reply says nothing about the result: confirm with 'Get a thing by its unique id'. Not marked destructive -- the thing survives and only the named fields move -- but it writes to real data with an admin-equivalent token and privacy rules do not apply.

api
bubble_io_post_obj_by_typenameWRITE

Create a thing, via POST /api/1.1/obj/{typename}. The body is a flat object of field name to value; `_id`, `Created Date` and `Modified Date` are set by Bubble and cannot be overridden, and a field left out takes the default configured on the type. Answers 201 `{"status": "success", "id": "..."}` -- that id is what every other tool here takes as `uid`. Creating a `user` is the special case: `Email` is required and `Password` optional, and this route creates the User WITHOUT returning an authentication token, so it cannot be used to sign anybody in. Use 'Create many things in one call' rather than looping this one for a batch.

api
bubble_io_post_obj_by_typename_bulkWRITE

Create many things in one call, via POST /api/1.1/obj/{typename}/bulk. THE BODY IS TEXT, NOT JSON: one complete JSON object per LINE, each with no newlines inside it, and the keys and values are exactly what 'Create a thing' takes. It is sent as `text/plain`, which the connector sets -- Bubble does not read this endpoint as JSON. PARTIAL SUCCESS IS THE NORMAL OUTCOME and this is the one thing to get right about it: the reply is one status LINE per input line, in order, each either `{"status":"success","id":"..."}` or an error for that line, and the call as a whole can answer 200 while individual lines failed. Read every line of the reply rather than the status code. Prefer this over looping 'Create a thing' for a batch.

api
bubble_io_put_obj_by_typename_by_uidWRITE

Replace every field on a thing, via PUT /api/1.1/obj/{typename}/{uid}. DESTRUCTIVE, and it is the entry on this provider that surprises people: any field you do NOT give a value is emptied or reset to the type's default. The thing keeps its `_id` and its `Created Date`, and everything else you did not send is gone -- there is no undo and no recycle bin. Use 'Change some fields on a thing' unless you genuinely mean to blank the rest; reach for this only when you hold the complete record. Answers 204 with an EMPTY body, so confirm by reading the thing back.

api

Put Bubble behind one governed endpoint.

Same permissions, same audit trail, whatever else you connect next.