# Airtable Base Schema Auditor & Link Checker (`mediocre_interest/airtable-schema-auditor`) Actor

Audit any Airtable base with a read-only token. Ranked findings for broken formulas, links into empty tables, duplicate field names, text columns holding dates, and invite links that give strangers edit access. Scorecard per base plus a shareable HTML report.

- **URL**: https://apify.com/mediocre\_interest/airtable-schema-auditor.md
- **Developed by:** [Mediocre\_Interest](https://apify.com/mediocre_interest) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / base audited

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Airtable Base Schema Auditor & Link Checker

**Audit any Airtable base with a read-only token and get a ranked list of what is wrong with it** — broken formulas, links into empty tables, near-duplicate field names, text columns holding dates, credentials typed into field descriptions, and reusable invite links that hand strangers edit access. Every base also gets a health score out of 100 and a shareable HTML report you can send to whoever owns it.

Press **Start** with nothing filled in and the Actor audits two bundled sample bases — one deliberately broken, one correctly built — so you can read real output before connecting an Airtable account. No LLM and no guesswork: the same schema always produces the same findings.

- **One read-only scope.** The whole rule set runs on `schema.bases:read`. Record sampling is the only thing that asks for a second scope, and it is off by default.
- **26 checks across five packs**, each finding carrying the evidence it read, so you can verify any one of them in your base in seconds.
- **Nothing is written to your Airtable.** Every request is a `GET`.

### What does the Airtable Base Schema Auditor do?

[Airtable](https://airtable.com) makes it easy to add a field and hard to notice the cost later. Delete a column a formula reads and the formula keeps its place in every view, showing an error in every record. Rename a column with a trailing space and Airtable accepts it as a second, separate field, because it only blocks exact, case-insensitive duplicates. Share a base with a reusable invite link and there is no page that lists every link still outstanding across your workspace.

This Actor reads a base's **schema** — the same structure Airtable's metadata API returns — and runs 26 fixed rules over it. Optionally it also samples a few records per table, which is what lets it tell an always-empty column from a sparse one and a text field holding dates from a real text field. Every finding is one row naming the table, the field, the evidence and the fix. Every base also gets a **scorecard**: a score out of 100, a grade, and an honest account of which checks could and could not run against your token.

The exposure checks are the ones without an equivalent anywhere in Airtable's own interface. A base's invite links, its collaborator domains and its workspace sharing settings are each visible one dialog at a time; this reads all of them across every base a token can see, in one run.

#### Which Airtable base problems does it find?

Every finding carries its rule id, its pack and a `confidence` of `high` (read straight off the schema), `medium` (measured from a record sample) or `low` (an English-language name heuristic).

**Exposure** — who can reach this base, and what leaves it when they do

| Rule                             | Severity | What it catches                                                                                                                                                                                                |
| -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `secret-in-description`          | critical | A credential-shaped string in a field or table description — Stripe, Slack, GitHub, AWS, Google, Airtable keys and bearer tokens. The finding reports the pattern, the offset and the length, never the value. |
| `open-invite-link`               | critical | A reusable invite link granting `edit` or above to anyone holding the URL, with no email-domain restriction. `high` when the link is read-only.                                                                |
| `collaborators-across-domains`   | medium   | The base is shared with people from two or more email domains. Carries domains and counts only — no address ever reaches the output.                                                                           |
| `workspace-shares-unrestricted`  | medium   | Anyone with edit access in the workspace can create a public share link.                                                                                                                                       |
| `lookup-exposes-pii`             | medium   | A lookup pulls a personal-data field across a link into another table, where different people can see it.                                                                                                      |
| `pii-field`                      | low      | A field name implies personal data (`email`, `phone`, `date of birth`, `iban`, and similar).                                                                                                                   |
| `workspace-invites-unrestricted` | low      | Anyone with edit access in the workspace can invite new collaborators.                                                                                                                                         |
| `domain-restricted-invite-link`  | low      | A reusable invite link exists but is restricted to named domains — worth confirming they are still the right ones.                                                                                             |

**Links** — relationships and the computed fields riding on them

| Rule                     | Severity | What it catches                                                                                                                                                                                         |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `broken-computed-field`  | critical | A formula, rollup, lookup or count Airtable reports as invalid. Two causes: the field it read was deleted, or one side of a link it travelled along was. Every record shows an error until it is fixed. |
| `orphan-table`           | medium   | A table with no link fields out and nothing linking in.                                                                                                                                                 |
| `link-target-unreadable` | medium   | A link points at a table this token cannot see, so the relationship is invisible to the audit.                                                                                                          |
| `self-referential-link`  | low      | A table links to itself. Legitimate for hierarchies — reported once per relationship, not once per field.                                                                                               |

**Values** — what the records say that the schema cannot *(needs record sampling)*

| Rule                      | Severity | What it catches                                                                                                                                                |
| ------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text-field-wrong-type`   | high     | A text column whose every sampled value is a date, an email address, a URL or a number. A text column holding dates sorts alphabetically.                      |
| `duplicate-primary-value` | high     | Two records sharing a primary-field value. Airtable does not enforce uniqueness there, and the primary field is how every link and lookup identifies a record. |
| `link-to-empty-table`     | high     | A link into a table that returned no records, so it can never resolve to anything.                                                                             |
| `empty-table`             | medium   | A table with no records at all.                                                                                                                                |
| `always-empty-field`      | medium   | A column nobody has ever filled in, across every sampled record.                                                                                               |

**Naming**

| Rule                            | Severity | What it catches                                                                                                                                        |
| ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `duplicate-field-name`          | high     | Fields in one table whose names differ only in spacing, separators or case — `Client Name`, `Client  Name`, `Client Name `. Airtable allows all three. |
| `duplicate-field-across-tables` | medium   | The same field name in two or more tables, counted by table rather than by field.                                                                      |

**Structure**

| Rule                     | Severity | What it catches                                                                                                                                                   |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `select-should-be-table` | medium   | A single- or multi-select with 20 or more options. A list that long is a table wearing a disguise.                                                                |
| `table-named-after-date` | medium   | A table named after a year, month or quarter — a snapshot nobody will clean up.                                                                                   |
| `inconsistent-precision` | low      | Number, currency or percent fields in one table rounding to different decimal places, so totals stop matching their rows.                                         |
| `table-no-description`   | low      | A table with nothing saying what one record represents.                                                                                                           |
| `no-field-descriptions`  | low      | A table of five or more fields where not one is described.                                                                                                        |
| `default-table-name`     | low      | `Table 1`, `Untitled`, `Grid 2` — a table nobody has named.                                                                                                       |
| `default-field-name`     | low      | A table still carrying three or more of Airtable's starter field names, none described and none filled in; or a numbered name Airtable generated, like `Field 7`. |

### Why use the Airtable Base Schema Auditor?

- **The scope ask is one line.** Everything above, including both `critical` exposure checks, runs on a token holding `schema.bases:read` and nothing else. Turn on record sampling and it asks for `data.records:read` as well. Nothing needs write access, and no request is anything but a `GET`.
- **Nothing to install, and no credentials needed to start.** Press Start and read the two bundled sample bases. Connect a token when you want your own.
- **Every finding shows its work.** The three colliding field names, the 28 select options, the four sampled values that were all dates — the `evidence` column carries what the rule actually read, so a false positive is obvious in seconds rather than trusted by default.
- **A credential it finds is never republished.** A dataset is shareable by URL, so `secret-in-description` reports the pattern it matched, how long it was and where it sat — and never the string itself. Collaborator findings carry domains and counts, never email addresses.
- **Built for agencies and consultants.** One run audits every base a token can see, in parallel, and writes one scorecard row per base you can sort by grade. Workspace-wide settings are reported **once per run**, not once per base, so auditing forty bases in one workspace does not produce the same two findings forty times. Schedule it on Apify for a monthly sweep, export to JSON, CSV or Excel, or call it from the API.

### How to audit an Airtable base

The free path, with no Airtable account at all:

1. Open the Actor and press **Start** without filling anything in.
2. Read the **Findings** table in the Output tab — worst first, with the evidence beside each row.
3. Open **Scorecards** for the two sample bases: a broken one at 0/F and a well-built one at 97/A.
4. Open one of the **audit reports** for the same thing as a page you could hand to a client.

To audit your own bases:

1. Go to [airtable.com/create/tokens](https://airtable.com/create/tokens) and create a personal access token.
2. Give it the **`schema.bases:read`** scope. Add **`data.records:read`** as well if you want the value checks.
3. Under **Access**, add every base you want audited — a personal access token lists its bases one by one, and a base you do not add is invisible to the token.
4. Paste the token into **Airtable personal access token** and press Start. Leave **Bases to audit** empty to audit everything the token can see.
5. Switch on **Sample records** if you granted the second scope.

### Input

| Field                              | What it does                                                                                                                            |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Airtable personal access token** | Your read-only token. Leave it empty to audit the two bundled sample bases for free.                                                    |
| **Bases to audit**                 | Base IDs, one per line. Paste a whole base URL if that is easier — the ID is taken out of it. Empty means every base the token can see. |
| **Rule packs**                     | Which of the five families to run: structure, naming, links, exposure, values.                                                          |
| **Minimum severity**               | Drop findings milder than this from the output. It does **not** change the score — see below.                                           |
| **Sample records**                 | Read a few records per table so the value checks can run. Needs `data.records:read`. Off by default.                                    |
| **Records sampled per table**      | How many records to read from each table. Default 100. Below 3 the wrong-column-type check cannot fire.                                 |
| **Maximum tables sampled**         | Stop sampling after this many tables per base. Schema checks still cover every table. Default 200.                                      |
| **Write a shareable HTML report**  | Produce a self-contained page per base, linked from that base's scorecard row.                                                          |

Audit one base, with the value checks on:

```json
{
    "airtableToken": "<your Airtable personal access token>",
    "baseIds": ["appJlDPhIscwb0SAf"],
    "sampleRecords": true
}
```

Sweep every base a token can see, for exposure problems only:

```json
{
    "airtableToken": "<your Airtable personal access token>",
    "rulePacks": ["exposure"],
    "minSeverity": "high"
}
```

### Output

One row per finding in the default dataset, one row per base in the **Scorecards** dataset, and one HTML report per base plus the run totals in the key-value store. Download either dataset as JSON, CSV, Excel or HTML from the run's Output tab.

Pressing Start with nothing filled in produces 45 finding rows. This is one of them:

```json
{
    "source": "sample",
    "collectedAt": "2026-09-29T06:18:30.683Z",
    "baseId": "appJlDPhIscwb0SAf",
    "baseName": "A1 Audit Fixture (messy)",
    "tableId": "tblR61L0Lmcv5x2zP",
    "tableName": "Clients",
    "fieldId": "fldBzhbIDtVkKHsjv",
    "fieldName": "Vendor Key",
    "fieldType": "singleLineText",
    "rulePack": "exposure",
    "rule": "secret-in-description",
    "severity": "critical",
    "confidence": "medium",
    "evidence": "Matches a Stripe live secret key, 34 characters long, at position 32 of the field description. The value is deliberately not reproduced here.",
    "message": "The description of \"Clients.Vendor Key\" contains a credential-shaped string.",
    "remediation": "Remove it and rotate the key. Descriptions are schema, so everyone with read access to the base can already see it, and every export and integration carries it."
}
```

And this one, where Airtable itself allows all three spellings:

```json
{
    "tableName": "Clients",
    "rulePack": "naming",
    "rule": "duplicate-field-name",
    "severity": "high",
    "confidence": "high",
    "evidence": "\"Client Name\", \"Client  Name\", \"Client Name \"",
    "message": "Table \"Clients\" has 3 fields whose names differ only in spacing, separators or case.",
    "remediation": "Airtable only blocks exact, case-insensitive duplicates, so a trailing space or an underscore slips past. Merge them into one field and delete the spares - anyone filling in this table is currently choosing between them at random."
}
```

#### What does each finding row contain?

| Column                                            | Meaning                                                                                                   |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `severity`, `confidence`                          | `critical`, `high`, `medium` or `low`; and how far the finding can be trusted.                            |
| `rule`, `rulePack`                                | Which check fired, and which of the five families it belongs to.                                          |
| `baseName`, `tableName`, `fieldName`, `fieldType` | Where the problem is. Empty `baseName` marks a workspace-wide finding.                                    |
| `message`, `remediation`                          | What was found, and what to change in Airtable.                                                           |
| `evidence`                                        | What the rule actually read — the colliding names, the option count, the sampled values, the sample size. |
| `source`                                          | `airtable` for your bases, `sample` for the bundled demo.                                                 |
| `baseId`, `tableId`, `fieldId`, `collectedAt`     | Identifiers and the run timestamp, for joining runs together.                                             |

#### What does each scorecard row contain?

```json
{
    "baseName": "A1 Audit Fixture (messy)",
    "score": 0,
    "grade": "F",
    "tableCount": 6,
    "fieldCount": 40,
    "linkCount": 3,
    "recordsSampled": 16,
    "criticalCount": 3,
    "highCount": 8,
    "mediumCount": 17,
    "lowCount": 13,
    "findingCount": 41,
    "findingsReported": 41,
    "packsRun": ["structure", "naming", "links", "exposure", "values"],
    "packsSkipped": []
}
```

`linkCount` counts **relationships**, not link fields: Airtable stores one relationship as a field in each of the two tables, and the pair counts once. `findingCount` is everything the checks found; `findingsReported` is how many rows reached the dataset after **Minimum severity**. `reportUrl` links that base's HTML page.

#### What do packsRun and packsSkipped tell me?

They account for all five packs in three states, and the third is the one that matters: **`unknown` is not `clean`**. If Airtable returns no invite-link data for a base, the scorecard says so rather than reporting no invite links — an absent answer and an empty one mean opposite things, and a base with an open edit link reported as clean would be the worst thing this Actor could do. A token that can read a base but not its workspace gets `exposure` marked as run with the two workspace-wide checks noted as skipped.

#### How is the score calculated?

Each distinct rule costs its severity weight — 25 for `critical`, 10 for `high`, 4 for `medium`, 1.5 for `low` — multiplied by `1 + ln(number of findings)`, and capped at 25 so no single rule can sink a base. The total is subtracted from 100. Grades are A at 85 or above, B at 70, C at 55, D at 40, F below that.

Two properties follow, and both are deliberate. A rule broken forty times is one problem that is worse than one broken once but not forty times worse, so the score does not simply track base size. And **Minimum severity never changes the grade** — it decides what is written to the dataset, not what the base is worth. Two people auditing the same base with different filters get the same score.

The score is **modelled, not measured**. Use it to rank bases against each other and to watch one base over time, not as an absolute measure of quality. Workspace-wide findings are excluded from it, because they describe the workspace rather than any base inside it.

### How much does it cost to audit an Airtable base?

The Actor is **pay per event**, at the prices shown on this page, and there are two events:

| Event             | Charged                     | When                                                 |
| ----------------- | --------------------------- | ---------------------------------------------------- |
| `base-audited`    | Once per base               | After that base's findings and scorecard are written |
| `records-sampled` | Once per 1,000 records read | After the rows those records produced are written    |

A base is priced whole: the full cost is worked out from its schema before a single record is read, and a base your remaining credit will not cover is skipped and named rather than half-audited. Nothing is charged per finding — a linter paid per finding becomes a noisy linter. Nothing is charged for a base that could not be read, and the two bundled sample bases are free.

A typical run auditing ten bases with sampling off costs ten `base-audited` events. Runs are fast — the bundled demo finishes in about a second, and a real base is one request for its schema plus one per sampled table — so platform usage on top is a fraction of a cent.

### How to run the audit from the API or on a schedule

An audit takes seconds, so the Actor can run synchronously — one request in, findings out:

```bash
curl -X POST "https://api.apify.com/v2/acts/mediocre_interest~airtable-schema-auditor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "airtableToken": "<your Airtable personal access token>",
        "baseIds": ["appXXXXXXXXXXXXXX"],
        "sampleRecords": true
      }'
```

The response is a JSON array of the finding rows shown above. Add `&format=csv` for a spreadsheet, or `&fields=severity,rule,baseName,tableName,fieldName,message` to trim the columns. Send an empty body, `{}`, to get the sample findings without a token.

Apify's **scheduler** runs the same input weekly or monthly without any code — point it at your token and read the Scorecards table each time. The Actor is also available through Apify's [integrations](https://docs.apify.com/platform/integrations) for Make, n8n, Zapier and Slack, and through the Apify API clients for JavaScript and Python.

### Tips for better results

- **Start with Minimum severity at `low`.** The naming and documentation findings are most of what a consultant hands over, and the Output tab sorts by severity anyway.
- **Turn on Sample records for a base you actually own.** Half the value checks — always-empty columns, text fields full of dates, duplicate primary values — cannot fire without them, and they are the findings that surprise people.
- **Grant the token every base at once.** A personal access token lists its bases individually, so a sweep is only as complete as the Access list on the token.
- **Read `packsSkipped` before you trust a clean scorecard.** A base with no findings and `values: unknown` has not been checked for empty tables; it has been checked for everything else.
- **Re-run after fixing.** Rule ids are stable, so a diff of two runs is the list of what you fixed.

### FAQ

#### Is it safe to give this Actor an Airtable token?

The token is stored encrypted, never written to the output, and never logged — the run log shows `***`. The Actor only ever sends `GET` requests, and the scopes it asks for cannot write anything: `schema.bases:read` reads structure and `data.records:read` reads records.

#### Does it change anything in my Airtable base?

No. There is no write path in the Actor at all.

#### What scopes does my token actually need?

`schema.bases:read` for every check except the value pack, including both `critical` exposure checks. Add `data.records:read` only if you switch **Sample records** on. Both are read-only and available on every Airtable plan.

#### Does the exposure pack read my colleagues' email addresses?

It reads the base's collaborator list to count how many email domains it is shared across, and it writes **only the domains and the counts**. No address reaches a dataset row, a report, or a log line. If you would rather it did not read them at all, leave `exposure` out of **Rule packs**.

#### Why did the run finish with no findings?

Either the base is clean — the scorecard then says so with a high score — or **Minimum severity** filtered everything out. Check `findingsReported` against `findingCount` on the scorecard: when they differ, the gap is what the filter hid. A run whose rule packs cannot reach the chosen severity warns about it in the log before doing any work.

#### Does it use an AI model?

No. Every finding comes from a fixed rule over the schema, which is why the same base produces identical findings on every run and why a re-run after a fix is a reliable diff.

#### Why is a finding marked low confidence?

Because it came from an English-language word list. The personal-data names, the date-in-a-table-name check and the Airtable starter-name lists will miss a German or Japanese base entirely. They are graded `low` for exactly that reason rather than presented as certainties.

#### Can it audit a base I have only been shared, not created?

Yes, if you can add it to a personal access token under **Access**. The token's own permission level decides; Base read-only is enough.

#### What does it not check?

View filters, view sort configuration, share-link state and automations: Airtable's API does not expose any of them at any scope, so no Actor can read them. Interfaces are returned but nothing depends on them yet.

### What other Actors work with this one?

- [Make.com Scenario Auditor — Linter & Cost Review](https://apify.com/mediocre_interest/make-scenario-auditor) — the same idea for Make scenarios: ranked findings, a health score, and every finding priced in the operations it wastes.
- [n8n Workflow Auditor — Linter & Security Review](https://apify.com/mediocre_interest/n8n-workflow-auditor) — security, reliability and correctness findings for n8n workflows, one row per issue.
- [Make.com Operations Usage & Cost Analyzer](https://apify.com/mediocre_interest/make-operations-analyzer) — where a Make account's operations actually go, scenario by scenario.
- [n8n Workflow Backup & GitOps — Version History](https://apify.com/mediocre_interest/n8n-workflow-backup) — scheduled export of every n8n workflow to versioned, diffable JSON.

### Support

Found a false positive, a rule that should exist, or a base the Actor cannot read? Open an issue on the **Issues** tab. Include the `rule` id from the row and, if you can, the shape of the field or table that triggered it — never the base's contents. Custom rule sets and portfolio reports for agencies are available on request.

# Actor input Schema

## `airtableToken` (type: `string`):

A read-only Airtable personal access token. The whole rule set runs on the single <code>schema.bases:read</code> scope; add <code>data.records:read</code> only if you turn on record sampling below. Create one at <a href='https://airtable.com/create/tokens' target='_blank'>airtable.com/create/tokens</a> and grant it the bases you want audited.<br><br><b>Leave this empty to see the Actor work on two bundled sample bases</b> — one deliberately broken, one correctly built — for free, with no Airtable account. Those rows are marked <code>source: "sample"</code>.

## `baseIds` (type: `array`):

Airtable base IDs, one per line. Paste the whole base URL if that is easier — the ID is taken out of it. Leave empty to audit every base the token can see.

## `rulePacks` (type: `array`):

Which families of checks to run. <b>Exposure</b> reads the base's invite links and collaborator metadata (domains and counts only — no email address is ever written to the output). <b>Values</b> needs record sampling turned on below.

## `minSeverity` (type: `string`):

Drop findings milder than this. Leave it at <b>Low</b> on a first run: the naming and documentation findings are most of what a consultant hands over, and the output view sorts by severity anyway.

## `sampleRecords` (type: `boolean`):

Read a few records per table so the Actor can find empty tables, columns that are never filled in, text columns holding dates or emails, and duplicate values in a primary field. Needs the <code>data.records:read</code> scope on your token. Off by default so the standard run asks for exactly one read-only scope.

## `sampleSize` (type: `integer`):

How many records to read from each table. The sample is evidence, not a copy of your data — 100 is enough to tell an always-empty column from a sparse one. Values below 3 disable the wrong-column-type check.

## `maxTablesSampled` (type: `integer`):

Stop sampling after this many tables per base. Schema checks still cover every table; only the record-based checks stop. Airtable allows up to 1,000 tables in one base, and sampling all of them is one request each.

## `includeHtmlReport` (type: `boolean`):

Produce a self-contained web page per base, ranked worst first, ready to send to whoever owns the base. The link appears on that base's scorecard row.

## Actor input object example

```json
{
  "baseIds": [],
  "rulePacks": [
    "structure",
    "naming",
    "links",
    "exposure",
    "values"
  ],
  "minSeverity": "low",
  "sampleRecords": false,
  "sampleSize": 100,
  "maxTablesSampled": 200,
  "includeHtmlReport": true
}
```

# Actor output Schema

## `findings` (type: `string`):

Every problem the audit found, with the evidence the rule read and what to do about it.

## `scorecards` (type: `string`):

Each base's health score out of 100, its finding counts by severity, and which rule packs the token could run.

## `reports` (type: `string`):

A self-contained page per base, ranked worst first, ready to send to whoever owns the base.

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "baseIds": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("mediocre_interest/airtable-schema-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "baseIds": [] }

# Run the Actor and wait for it to finish
run = client.actor("mediocre_interest/airtable-schema-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "baseIds": []
}' |
apify call mediocre_interest/airtable-schema-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mediocre_interest/airtable-schema-auditor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HrKCgSdbfQo06BOUd/builds/IaNcC3ZaJlJzNuUtm/openapi.json
