# Airtable integration

> A ready-made recipe: meeting minutes and action items flow into your Airtable base automatically through the event stream.

Source: https://diko.app/en/api-docs/airtable

In about 10 minutes you connect Diko to Airtable: every finished meeting becomes a row in a **Susitikimai** (Meetings) table (title, date, duration, summary, decisions, a link back to Diko), and every action item from the minutes becomes a row in an **Užduotys** (Tasks) table, linked to its meeting. From there you can sort, filter, show them on a Kanban board or connect them to other workflows.

The recipe uses an Airtable automation with a **Run a script** action and our ready-made script [`diko-airtable-sync.js`](https://diko.app/examples/diko-airtable-sync.js). You do not need a server.

> **Note:**
> Diko also has a built-in Airtable connection (Integracijos) that sends the minutes without code. This recipe is for API customers who want to own the base structure: action items become separate rows, and the data stays in sync after changes in Diko.

## How it works

1. The Airtable automation runs the script every 15 minutes.
2. The script reads new events from [`GET /events`](https://diko.app/en/api-docs/reference/list-events.md), starting where the previous run stopped. It keeps that position (the cursor) in a **Diko nustatymai** (Diko settings) table.
3. On `meeting.ready`, `meeting.minutes.updated` or `meeting.updated` the script reads the current state of the meeting ([`GET /meetings/{meeting_id}`](https://diko.app/en/api-docs/reference/get-meeting.md), [`/minutes`](https://diko.app/en/api-docs/reference/get-meeting-minutes.md), [`/action-items`](https://diko.app/en/api-docs/reference/list-meeting-action-items.md)) and updates the rows. Several events about one meeting are merged into a single read.
4. `meeting.deleted` does not delete the row: it ticks **Ištrintas** (Deleted), and ticks **Pašalinta** (Removed) on the meeting's tasks.
5. The cursor only moves forward once a whole page of events is written to Airtable. Rows are matched by **Diko ID**, so running the script again never creates duplicates.

Why not a webhook? Airtable's **When webhook received** trigger cannot verify the Diko [webhook signature](https://diko.app/en/api-docs/webhooks.md#verifying-the-signature), so anyone who learns the address could write fake data. Reading the event stream with a key is safer and loses nothing: events are kept for 30 days. Diko records events only from the moment the account has a key that is not revoked or an enabled webhook endpoint, so meetings that finished earlier do not appear in the stream.

## 1. Prepare the base

Create a base (or use an existing one) with three tables. The names must match exactly. The defaults are Lithuanian; to use other names, change them in the `CONFIG` block of the script.

**Susitikimai** (Meetings)

| Field | Type |
|---|---|
| `Pavadinimas` (title) | Single line text (primary field) |
| `Diko ID` | Single line text |
| `Data` (date) | Date, with time |
| `Trukmė (min)` (duration) | Number, 1 decimal |
| `Santrauka` (summary) | Long text |
| `Sprendimai` (decisions) | Long text |
| `Nuoroda` (link) | URL |
| `Faktų patikra` (fact check) | Single line text, or Single select with `Patikrinta`, `Pataisyta`, `Nepatikrinta` |
| `Archyvuotas` (archived) | Checkbox |
| `Ištrintas` (deleted) | Checkbox |

**Užduotys** (Tasks)

| Field | Type |
|---|---|
| `Užduotis` (task) | Single line text (primary field) |
| `Diko ID` | Single line text |
| `Susitikimas` (meeting) | Link to another record → `Susitikimai` |
| `Atsakingas` (assignee) | Single line text |
| `Terminas` (due) | Single line text |
| `Būsena` (status) | Single select with `Atvira`, `Vykdoma`, `Atlikta`, `Atšaukta` (or Single line text) |
| `Šaltinis` (source) | Single line text |
| `Pašalinta` (removed) | Checkbox |

**Diko nustatymai** (Diko settings)

| Field | Type |
|---|---|
| `Pavadinimas` (name) | Single line text (primary field) |
| `Žymeklis` (cursor) | Single line text |
| `Paskutinis paleidimas` (last run) | Single line text |

Leave **Diko nustatymai** empty: the first run creates its single row. `Terminas` is text because a deadline in the minutes is often written in words ("by Friday"). For manual items the script writes the date as `YYYY-MM-DD`.

An English base only needs different names in `CONFIG`, for example:

```javascript
tables: { meetings: 'Meetings', tasks: 'Tasks', settings: 'Diko settings' },
meetingFields: { title: 'Title', dikoId: 'Diko ID', date: 'Date', durationMin: 'Duration (min)', summary: 'Summary', decisions: 'Decisions', link: 'Link', verification: 'Fact check', archived: 'Archived', deleted: 'Deleted' },
taskFields: { text: 'Task', dikoId: 'Diko ID', meeting: 'Meeting', assignee: 'Assignee', due: 'Due', status: 'Status', source: 'Source', removed: 'Removed' },
settingsFields: { name: 'Name', cursor: 'Cursor', lastRun: 'Last run' },
statusLabels: { open: 'Open', in_progress: 'In progress', done: 'Done', canceled: 'Canceled' },
verificationLabels: { verified: 'Verified', repaired: 'Repaired', unverified: 'Unverified' },
sourceLabels: { manual: 'Manual', generated: 'From minutes' },
```

> **Tip:**
> Before writing anything the script checks that every field exists. If one is missing, the automation run history tells you which field or option to add.

## 2. Create an API key

1. Open **Nustatymai → API** (Settings → API, [`/settings/api`](https://studio.diko.app/settings/api)) and choose **Sukurti raktą** (Create key). You need the Verslas plan.
2. Select only the **Susitikimai – skaityti** (`meetings:read`) scope. This recipe needs nothing else: the key can neither import nor read the library (see [Scopes](https://diko.app/en/api-docs/scopes.md)).
3. Name the key, for example `Airtable – sales base`, and choose an expiry (90 days by default).
4. Copy the key. **It is shown only once.**

## 3. Create the automation

1. In your Airtable base open **Automations** → **Create automation**.
2. Trigger: **At scheduled time**, for example every 15 minutes.
3. Action: **Run a script**.
4. Download [`diko-airtable-sync.js`](https://diko.app/examples/diko-airtable-sync.js) and paste all of it into the script editor.
5. In the left panel, under **Secrets**, add a secret named `DIKO_API_KEY` and paste the key. The script reads it with `input.secret('DIKO_API_KEY')`.
6. Turn the automation on.

> **Warning:**
> Never write the key into the script itself. Every editor of the base can read the script code, but not secret values. Airtable only lets people who can access a script's secrets edit that script.

By default the first run starts at the oldest event still kept (up to 30 days back). That covers meetings that finished after the account first had a key that is not revoked or an enabled webhook endpoint, not older ones: Diko did not record events for them. To start with new meetings only, set `startFrom: 'latest'` in `CONFIG`.

## 4. Test it

1. In the script editor press **Test** (or **Run test**).
2. The result shows a `summary`, for example how many meetings and tasks were created (`meetingsCreated`, `tasksCreated`) and why the run stopped (`stopReason`).
3. Check the tables: **Susitikimai** has one row per meeting, **Užduotys** rows are linked to it, and **Diko nustatymai** has a filled `Žymeklis`.
4. Run the test again. No new rows may appear: this proves that a repeated run is safe.

One run handles about 10–15 meetings, because Airtable allows a script at most 50 `fetch` requests and each meeting needs three. With more events `stopReason` is `budget reached`, and the next run continues where this one stopped.

## What the script does with your data

- **A meeting** is updated every time Diko reports a change. The summary, decisions and fact check are written only when the minutes are ready.
- **Manual action items** (created in Diko) have their own `act_...` ID, and their status follows Diko.
- **Action items from the minutes** have no ID, so the script builds a key from the meeting ID and the item text. Their status is set only when the row is created (Diko always reports them as `open`), so you can track it in Airtable afterwards.
- When the minutes are regenerated and an item is no longer in them, its row is marked **Pašalinta** (Removed), not deleted.
- You are free to add fields the script does not write: your comments, priorities or formulas stay untouched.

## Webhook alternative (less secure)

You can use the **When webhook received** trigger with a Diko [webhook](https://diko.app/en/api-docs/webhooks.md) endpoint pointing at the Airtable address. Airtable cannot verify the `webhook-signature` header, though, so anyone who has the address could send fake events. If you choose this path anyway, treat the webhook body only as a signal: never take data from it, always read the meeting through the API with a key, and keep the address secret.

## Troubleshooting

| Symptom | Cause and fix |
|---|---|
| `Diko rejected the API key (invalid_api_key)`, or `api_key_expired`, `api_key_revoked` | The key is wrong, expired or revoked (401). Create a new key and replace the `DIKO_API_KEY` secret. |
| `Diko denied access (insufficient_scope)` or `plan_required` | The key lacks `meetings:read`, or the account has no Verslas plan (403). See [Errors](https://diko.app/en/api-docs/errors.md). |
| `stopReason: rate limited ...` | The API returned 429. The script waits for `Retry-After` and retries; if the wait is too long it stops and continues next time. Do not run several automations with the same key at once: the API allows 2 concurrent requests per account, and a team plan shares 3 across the whole team (see [Rate limits](https://diko.app/en/api-docs/rate-limits.md)). |
| `Table "..." is missing fields` or `Add these options to ...` | Field names or options do not match. Fix the base or `CONFIG`. |
| The run succeeds but no rows appear | No meeting finished in the last 30 days or after the key was created, the key belongs to another account, or `startFrom: 'latest'` is set. Check the key with [`GET /me`](https://diko.app/en/api-docs/reference/get-me.md) and finish a new meeting. |
| `The stored cursor is no longer valid` | The cursor is too old or damaged (400 `invalid_cursor`). Clear the `Žymeklis` field and run again. |

**Starting over.** Clear the `Žymeklis` field in **Diko nustatymai**. The next run reads the events of the last 30 days again and updates the existing rows without creating duplicates.

## Security

- **Least privilege:** a key with `meetings:read` only, used only for this integration. Revoking it touches no other system.
- **A secret, not code:** the key lives only in the Airtable secret. Airtable redacts secrets from `console.log` output, and the script never writes the key anywhere.
- **Expiry and rotation:** choose a limited expiry. Diko sends a reminder 7 days and 1 day before; then [rotate the key](https://diko.app/en/api-docs/security.md#key-rotation-recipe) and update the secret.
- **Base access:** minutes can contain sensitive information, so share the base only with people who can see those meetings in Diko.
- Suspect a leak? Revoke the key right away under **Nustatymai → API** ([`/settings/api`](https://studio.diko.app/settings/api); see [Security](https://diko.app/en/api-docs/security.md)).
