# Base76 > Base76 is a database built for LLMs and agents. A base is a small, private, structured database (recipes, a CRM, notes, a reading list) with typed collections, relations, history, search, shareable pages, and realtime updates. Agents read and write it through MCP or REST; people browse it at https://app.base76.com. ## Connect - MCP (preferred for assistants): https://mcp.base76.com/mcp (streamable HTTP). Sign in with OAuth (the client discovers it from the 401 and /.well-known/oauth-protected-resource), or send an API key as `Authorization: Bearer b76_...`. - REST: https://api.base76.com/v1. Send `Authorization: Bearer b76_...` (an API key from Settings > API keys) or an OAuth token issued for https://api.base76.com/v1. JSON in, JSON out. OpenAPI: https://api.base76.com/openapi.json. - Each base has its own guide with real collections, fields, and examples: https://api.base76.com/v1/bases///llms.txt (and /openapi.json). ## Working with bases 1. `list_bases` shows the bases you can use and who can see each one (its audience). 2. `describe` a base before writing: read its README, collections, field keys and types. 3. Filter with `find` (where/sort/select/include) instead of fetching everything; use `aggregate` for totals and `search` when you don't know which field holds the answer. 4. Every write names its base and returns `audience`. If it is not "private", tell the user who else can see the data. 5. Destructive schema changes, drop_collection, and restore_base answer `confirm_required` with the impact first. Show it to the user and resend with `confirm: true` only if they agree. 6. Mistakes are recoverable: `history` + `restore` for records and import batches, `restore_base` for point-in-time (30 days). ## Addressing - A base is `workspace/base`, e.g. `tim/recipes`. In REST paths: `/v1/bases/tim/recipes/...`. - Collection and field keys: lowercase `snake_case`. System fields: `id`, `_version`, `_created_at`, `_updated_at`, `_created_by`, `_updated_by`. - Relations accept a record id or a natural key on a unique field: `{"cuisine": {"name": "Indian"}}`. ## Pagination Every list returns `{"items": [...], "next_cursor": "..." | null, "has_more": true|false, "seq": 123}`. Pass `cursor: next_cursor` with the same query for the next page. `limit` defaults to 50, max 200 (search: 10, max 50). `seq` is the base's change sequence at read time. ## Errors `{"error": {"code", "message", "details"?: [{"path", "message", "fix"?}], "impact"?, "current"?, "limit"?}}`. Codes and HTTP status: bad_request 400, unauthorized 401, quota_exceeded 402, forbidden 403, not_found 404, conflict 409, confirm_required 409, validation_failed 422, rate_limited 429, internal 500. Messages say what to do next; apply each `fix` and retry. ## Examples (REST) ```sh curl -H "Authorization: Bearer $B76_KEY" https://api.base76.com/v1/bases curl -H "Authorization: Bearer $B76_KEY" https://api.base76.com/v1/bases/tim/recipes curl -X POST -H "Authorization: Bearer $B76_KEY" -H 'content-type: application/json' \ -d '{"collection":"recipes","where":{"rating":{"gte":4}},"sort":"-rating","limit":10}' https://api.base76.com/v1/bases/tim/recipes/find curl -X POST -H "Authorization: Bearer $B76_KEY" -H 'content-type: application/json' \ -d '{"record":{"title":"Chana masala","rating":5}}' https://api.base76.com/v1/bases/tim/recipes/records/recipes curl -X POST -H "Authorization: Bearer $B76_KEY" -H 'content-type: application/json' \ -d '{"body":"Decided to ship on Friday.","tags":["decisions"]}' https://api.base76.com/v1/notes ``` GET and DELETE take their inputs as query parameters (arrays comma-separated, objects as JSON); other methods take a JSON body. Path segments fill `base`, `collection`, and `id`. ## Bulk import `import_session` returns an upload URL. `PUT ` with a JSON record or any file; `POST ` with NDJSON (one record per line, optional `"_key"` for idempotency). Undo with `restore` `{"batch": ""}`. ## Operations - `list_bases` (GET /v1/bases, scope read): List the bases you can access, each with its audience (who can see it). Call this first, then describe the base you need. - `describe` (GET /v1/bases/:ws/:base, scope read): Get a base's README, collections with field keys/types/ids, indexes, import status, and search index lag. Read the README first: it records the base's purpose and conventions. Call before writing to a base you have not described in this conversation. - `create_base` (POST /v1/bases, scope admin): Create an empty base in a workspace. Use for a new, separate body of data (a CRM, a recipe box); add collections to an existing base when the data relates to it. Write a README with update_base so later agents know its purpose. - `update_base` (PATCH /v1/bases/:ws/:base, scope schema): Change a base's name, description, or README. Keep the README current when you add collections or conventions. - `delete_base` (DELETE /v1/bases/:ws/:base, scope admin): Delete a whole base. It disappears at once for everyone (people, agents, keys, share links) and is purged with its files after 30 days; until then its owner can restore it in the app. Without confirm: true it returns the impact; show the user and confirm the exact base first. The personal notes base cannot be deleted. - `define_collection` (PUT /v1/bases/:ws/:base/collections/:col, scope schema): Create or replace a collection's schema (full definition each time). Additive changes apply at once. Removing fields, changing types, dropping used choices, or tightening rules returns confirm_required with the records affected: show the user, then resend with confirm: true. To rename a field, keep its id and change its key. - `drop_collection` (DELETE /v1/bases/:ws/:base/collections/:col, scope schema): Delete a collection and all its records. Without confirm: true it returns the record count; show the user before confirming. Recoverable only with restore_base. - `find` (POST /v1/bases/:ws/:base/find, scope read): Query one collection with filters, sort, field selection, and related-record includes. Filter in the query rather than fetching everything. where: {"rating": {"gte": 4}, "tags": {"has": "dinner"}, "or": [...]}; relations: {"cuisine": {"name": "Indian"}} or {"recipes": {"some": {...}}}. sort: "-rating" (default newest first). Pages with next_cursor. - `aggregate` (POST /v1/bases/:ws/:base/aggregate, scope read): Count, sum, average, min, max, or count distinct over a collection, optionally grouped (up to 3 fields). Use for totals and reports instead of fetching records. metrics: {"n": {"count": true}, "avg_rating": {"avg": "rating"}}. - `search` (POST /v1/bases/:ws/:base/search, scope read): Full-text and semantic search across a base (or chosen collections). Use when you don't know which field holds the answer or the wording differs; use find for exact filters. Returns ids, titles, snippets, and scores; pass ids to find for full records. Keyword results are current at once; semantic and hybrid results trail writes by about 2 minutes (describe's index_lag.semantic_ready_after). - `get` (GET /v1/bases/:ws/:base/records/:col/:id, scope read): Fetch one record by id, optionally expanding relations. - `create` (POST /v1/bases/:ws/:base/records/:col, scope write): Create one record (record) or up to 500 (records) in a collection. Relations take ids or a natural key like {"cuisine": {"name": "Indian"}}. The result states the base's audience: tell the user if it is not private. - `update` (PATCH /v1/bases/:ws/:base/records/:col/:id, scope write): Change some fields of a record; null clears a field. Pass if_version (the record's _version) when a person may be editing too; a mismatch returns conflict with the current record. Prior versions stay in history. - `upsert` (PUT /v1/bases/:ws/:base/records/:col, scope write): Create or update up to 500 records matched by id or by a unique field (on). Use for syncing data from elsewhere without duplicates. - `delete` (DELETE /v1/bases/:ws/:base/records/:col/:id, scope write): Delete a record. Relations decide what happens to records linking to it (restrict refuses, set_null clears, cascade deletes). Recoverable with restore. - `history` (GET /v1/bases/:ws/:base/records/:col/:id/history, scope read): List a record's versions, newest first, with who changed what. Use before restore. - `restore` (POST /v1/bases/:ws/:base/restore, scope write): Undo at record level: restore a record (including a deleted one) to a prior version by writing a new version, or undo an import by deleting every record from its batch (the import session id). - `restore_base` (POST /v1/bases/:ws/:base/restore-base, scope admin): Rewind the whole base to a moment in the last 30 days (or to an undo_bookmark from an earlier restore_base). Every change after it is lost unless undone with the returned undo_bookmark. Requires confirm: true after the user agrees. - `set_template` (PUT /v1/bases/:ws/:base/collections/:col/template, scope schema): Customize how a collection's record pages or collection page render (HTML and markdown links), as a list of blocks. null restores the default derived from field types. - `share` (POST /v1/bases/:ws/:base/shares, scope admin): Create, list, or revoke read-only share links to a whole base, a collection, or one record. Anyone with a link can read what it covers; confirm with the user before creating one. - `list_guests` (GET /v1/bases/:ws/:base/guests, scope admin): List the people invited to a base as guests, with their role (editor or viewer). - `add_guest` (POST /v1/bases/:ws/:base/guests, scope admin): Give another Base76 user access to this base by email: editors can read and write records, viewers can only read. This grants access to everything in the base and changes its audience; confirm the person and role with the user first. Re-adding changes the role. - `remove_guest` (DELETE /v1/bases/:ws/:base/guests, scope admin): Revoke a guest's access to a base, by email or user_id. - `import_session` (POST /v1/bases/:ws/:base/imports, scope write): Get a short-lived upload URL for appending many records or files to one collection from a script (curl, no SDK). PUT with a JSON record, or any file to create a record holding it; POST with NDJSON for many. Reruns are idempotent by client key; undo with restore {batch: session_id}. - `export` (POST /v1/bases/:ws/:base/export, scope admin): Start a full export (schema, NDJSON per collection, files). The download link goes to the user in the app; only use when the user asks for an export. - `save_note` (POST /v1/notes, scope write): Save a markdown note to your personal notes base with no setup. Use to offload a conversation, decision, or snippet the user wants kept. - `upload_file` (POST /v1/bases/:ws/:base/files, scope write): Get a presigned URL to PUT one file (15 minutes), then set the returned file id on a record's file field. For many files use import_session.