# Auto Appy Custom App and Database API Source-checked on 2026-09-22. This guide describes the checked-out implementation; deployment may be older. Backend revision before documentation: `e923148a`. ## Authentication and independent use These APIs can be called from any server-side HTTP client; no workflow node, integration template, or Gemini call is required. Use the API host below, not the product website. The examples use fictional records and placeholders. No request shown here was sent to production during this implementation. Create an application-specific API key through the signed-in app's **API keys** page. Supply `Authorization: Bearer `, `Accept: application/json`, and `X-AutoAppy-Workspace: `. Add `Content-Type: application/json` for JSON writes. The key is a personal credential: current application access, organisation membership and resource permissions still apply. Use a read/write key for writes; a read-only key does not become writable by selecting an organisation. Keys cannot create more keys or bypass interactive management restrictions. Set finite client timeouts, keep TLS verification enabled, and do not follow redirects with credentials. Store the key in a server-side environment/secret store. The Postman collection has an empty `api_key` variable; set it locally as a secret. Do not export it after adding a key. The collection contains writes: send only the requests you intend to execute; do not run the whole collection against production. ## Errors and debugging | Result | Meaning / next check | | --- | --- | | 200 / 201 | API read/create accepted; inspect business status in the response. | | 202 | Work accepted asynchronously, not finished. Read its status/result. | | 401 | Missing, invalid, expired or revoked credential; wrong auth mechanism may also cause this. | | 403 | Wrong application key, read-only write, missing membership, role, record access, or interactive-only endpoint. | | 404 | Wrong local ID or inaccessible record. Do not substitute an ID from another organisation. | | 409 | Version conflict or incompatible repeated operation. Fetch current state before deciding how to proceed. | | 413 / 422 | Payload bounds or validation failure. Inspect the direct API's `message` and `errors`. | | 429 | Rate limit. Honor `Retry-After` when present. Do not automatically repeat a write. | | 5xx / timeout | Dependency or server failure; a write may already have happened. Inspect state before retrying. | Central API key checks allow up to 120 requests/minute/key by default; module validation and platform subrequests consume additional calls. Endpoint-specific limits and AI orchestration limits also apply. A successful request is not proof of WhatsApp delivery or of completed AI processing. To isolate a bug: 1. Call the documented read endpoint directly with the same app key, central organisation and resource IDs. Record method, path, timestamp, status and a redacted response. 2. Compare with this guide and the endpoint's linked controller/request validator. If the documented contract disagrees with deployed code, record both revisions and correct the contract or implementation deliberately. 3. If direct API succeeds but the workflow fails, compare the mapped input, saved connection tag, selected action name, path/query/body mapping, and source API host. Never paste a key into an issue or application log. 4. If the API accepts work but the result is wrong, inspect the owning app's job/message/turn history and downstream provider status. The integration must preserve these distinctions. The [route reference](ROUTES.md), [OpenAPI specification](openapi.json) and [Postman collection](postman_collection.json) describe native application operations independently of the workflow catalog. [Coverage](coverage.json) accounts for included operations and exclusions. [Lookup recipes](lookup-guide.md) provide field descriptions, pagination, complete requests, responses and error handling. Third-party adapter endpoints are internal-only and absent from all public downloads. Sensitive control-plane endpoints are excluded; the exact internal audit remains in the repository. Browser-only product routes are explicitly marked and do not imply API-key access; SSO and CSRF requirements remain enforced. ## Endpoint and authentication contract API root: `https://api.autoappy.com`. Website: `https://autoappy.com`. Use an Auto Appy **main** API key for the operations in this guide. This is the hosted custom-app database API, not an external MySQL connection or arbitrary SQL execution service. The prefix is `/api/v1/platform/organisations/{workspace}/data`, where `{workspace}` is the **central organisation UUID**, also supplied in `X-AutoAppy-Workspace`. Current data permissions, app/database visibility and resource ownership remain enforced. App IDs use the existing database identity; table IDs and record IDs are UUIDs. Friendly organisation/app/sheet slugs are not interchangeable with these IDs on UUID routes. Scoped developer App Tokens are a different credential with explicit selected database/API/model grants and scopes; do not paste one into an API-key connection. See the existing App Development and Portable Applications guides for that mode. The existing built-in `autoappy_data/runDataApi` workflow action can run a published saved API under the workflow owner's current permissions without a new key; it remains available independently of this richer key-based app catalog. ```sh curl --fail-with-body "https://api.autoappy.com/api/v1/platform/organisations/$AUTOAPPY_ORGANISATION_ID/data/databases" \ -H "Authorization: Bearer $AUTOAPPY_API_KEY" \ -H "X-AutoAppy-Workspace: $AUTOAPPY_ORGANISATION_ID" -H 'Accept: application/json' ``` ## Discover apps, tables and fields - `GET /databases` under the prefix returns `data[]` of visible databases/apps with stable IDs and names. - `GET /bootstrap?database_id={database_uuid}` returns `data.databases`, `data.tables`, types and date formats. Table listing is bounded at500, with `data.truncated=true` if incomplete. Do not silently treat this as a complete directory. - `GET /tables/{table}` returns `data` with table `id`, `database_id`, `name`, `type`, `status`, `fields`, `version` and access flags. Fields have stable `key`, readable `label`, `type`, `required`, `active`, optional `options`, and relation/contact metadata. Types include text, number, boolean, date, datetime, select, email, url, json, contact and relation. Use ISO dates by default (`YYYY-MM-DD`); datetimes include a timezone. Relations/contacts use arrays of authorized IDs, even for a single relationship. Inactive fields cannot be written. The table schema is authoritative; user-provided labels never create new columns. ## Read and write records | Method and path under the prefix | Behavior | | --- | --- | | `GET /tables/{table}/records` | Paginated list with optional search/filters; `page>=1`, `per_page=1..100` (default30). Returns `data`, total, current_page, last_page, per_page. | | `GET /tables/{table}/records/{record}` | Single accessible record in `data`, including current version. | | `POST /tables/{table}/records` | Create a record; name required, values optional and schema-validated. | | `PATCH /tables/{table}/records/{record}` | Update only supplied fields; requires current `expected_version`. | | `GET /tables/{table}/aggregate?operation=count` | Count or sum/avg/min/max of an authorized numeric field. | Create request, with fictional keys that must exist in your schema: ```json {"name":"Example order","values":{"amount":0,"paid":false,"notes":null},"external_key":"example-order-123"} ``` Name is a string up to255. `values` has at most100 fields and100KB of data. Unknown/inactive fields and invalid types fail validation. Optional `external_key` is at most190 characters and unique within the table; collision returns409. It is a useful duplicate-detection key, not an automatic upsert. A repeated POST without a unique external key can create another record. Update request: ```json {"expected_version":3,"values":{"paid":true}} ``` Omitted values remain unchanged, `false` and `0` remain values, and explicit null can clear a nullable value. `expected_version` must come from the latest read; stale versions return409. Optional `schema_version` fences against a changed table schema. Contact tables retain canonical contact identity: use `contact_id` for an existing person or the supported `contact` object for creation, and `expected_contact_version` when modifying the canonical profile. Renaming a contact-backed row can update that profile. Do not treat contact tables like anonymous standard rows. Record writes can emit configured table webhooks. API success means the record operation succeeded; inspect webhook receipts/status separately where applicable. ## Models, saved APIs, app development and deletion Direct model operations are `POST /models/{table}/run` and app-keyed `POST /apps/{app}/models/{key}/run`. Saved published APIs use `/apis/{artifact}/run`; the method and arguments are defined by their saved contract. Source/devkit export, versioned source sync, schema plan/apply and seed plan/apply are developer APIs with their own review/version tokens. App/database/table deletion uses explicit deletion plans/versions and can cascade. Those operations are documented in the existing `AUTO-303`, `AUTO-308`, `AUTO-310`, and Data Deletion sources linked from the complete route reference; they are not silently invoked by a record action. ## Sheet creation, row lookup and deletion The API contract is broader than the workflow picker. Setup APIs remain in the complete route reference even when no workflow action is provided. `POST /tables` creates a table in an existing database. Supply `database_id`, `name` (maximum 120 characters), optional `description`, `type` (`standard` or `contact`), `fields` and `layout`. Table management permission and database access are required. The workflow action uses `standard` and `layout.mode=sheet`; standalone callers can choose other supported layouts. Example: ```json {"database_id":"33333333-3333-4333-8333-333333333333","name":"Orders","type":"standard","fields":[{"key":"amount","label":"Amount","type":"number"},{"key":"paid","label":"Paid","type":"boolean"}],"layout":{"mode":"sheet"}} ``` `GET /tables/{table}/records` accepts `q` for a row-name substring, **not `search`**, and `external_key` for an exact match. Optional `filters` is an array of up to 10 `{field,operator,value}` objects using `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `contains` or `empty`. Encode nested GET parameters as `filters[0][field]=paid&filters[0][operator]=eq&filters[0][value]=0`. Types and supported operators depend on the field; JSON filtering is unsupported and relationships support equality only. `status` accepts active/archived/all; `sort` and `direction` control ordering. The same filters apply to aggregate. Numeric operations require an active numeric `field`; count does not. `DELETE /tables/{table}/records/{record}` takes JSON `{"expected_version":3}` and returns deleted row identity in `data`. It permanently removes the row and dependent links/history/follow-up data; it does not delete the canonical contact. Fetch the current version first. Version conflicts must be resolved deliberately, not bypassed by automatic retry. These are data operations, not arbitrary SQL queries.