Skip to content

Airtable ​

Finds, creates, completes and comments rows in an Airtable base, and drops the attachments (from the email or added by a step) into it.

The Airtable node finds, creates, completes and comments rows in an Airtable base, and drops files into an attachment field. Use it to keep a case register in step with your mailbox: one row per request, completed as emails arrive, without duplicates.

It works with a personal access token stored once in Connections. See Airtable to create the connection and choose the scopes. To start a workflow when a row changes, use the Airtable row changed trigger. For data that only lives inside Mankomail, the built-in tables are simpler.

The base, the table, the view and the fields are chosen from lists loaded from your Airtable account. The workflow stores their ids (app…, tbl…, fld…, viw…), so renaming a table or a column in Airtable does not break it. Changing the base empties the table, and changing the table empties the fields that depend on it.

At a glance ​

  • Type: airtable.api · version 1
  • Category: Data
  • Kind: Step — one stage of a run
  • Effect: Writes outside (external_write) — writes outside Mankomail; described instead of performed during a test run
  • Needs a carrier email: No
  • Connection: Airtable
  • Inputs: main
  • Outputs: main

Connection ​

This node needs a Airtable connection.

Parameters ​

connection ​

Airtable connection — The Airtable connection to use. Create it once in Connections; its key never appears in the workflow.

  • Type: Connection (credential)
  • Required: Yes
  • Default: "" (empty)

resource ​

Resource

  • Type: One choice (options)
  • Required: Yes
  • Default: record
  • Options:
    • record — Record: A row in a table: find it, read it, create it, complete it, comment it.
    • attachment — Attachment: Drop an email attachment into an attachment field, or list the ones on a record.
    • base — Base: The accessible bases, and one base’s schema (tables, fields, views).

recordOperation ​

Operation

  • Type: One choice (options)
  • Required: Yes
  • Default: search
  • Options:
    • search — Search: The rows matching a filter, a view, a sort.
    • get — Get a record: By its rec… id. Not found is not an error: found is false.
    • create — Create: Adds a row. ⚠️ A retry of the step adds a second one: prefer “Create or update”.
    • upsert — Create or update: Matches on a key field: updates the row if it exists, creates it otherwise. The clean way to make a workflow safe to retry.
    • update — Update: Changes a row known by its id.
    • delete — Delete: Sends the row to the base trash.
    • comment — Comment: Adds a comment on the row. @[usrXXXX] mentions a collaborator.
  • Shown when: resource is record

attachmentOperation ​

Operation

  • Type: One choice (options)
  • Required: Yes
  • Default: upload
  • Options:
    • upload — Upload the attachments: Those of the email and the files added by an earlier step. The bytes go straight to Airtable: the document is never exposed on a public URL.
    • list — List a record’s attachments: Name, size, type and download link. ⚠️ Links expire after two hours.
    • download — Fetch a record’s attachments into the run: Fetches the files into the run’s attachments: a “Compose an email” can then attach them by their position.
  • Shown when: resource is attachment

maxDownloadMb ​

Maximum size per file (MB)

  • Type: Number (number)
  • Required: No
  • Default: 10
  • Whole number, from 1 to 10
  • Shown when: resource is attachment and attachmentOperation is download

baseOperation ​

Operation

  • Type: One choice (options)
  • Required: Yes
  • Default: schema
  • Options:
    • schema — Get a base schema: Its tables, their fields and views, with the tbl…, fld…, viw… ids.
    • list — List the bases: The ones the token can reach, with their permission level.
  • Shown when: resource is base

base ​

Base — The Airtable base. The “URL” mode accepts an airtable.com/appXXXX/… address.

  • Type: Remote resource (resourceLocator)
  • Required: Yes
  • Ways to choose: pick from a list, type an ID, paste a URL (airtable.base)
  • Listed with the connection in: connection
  • Shown when: resource is record or resource is attachment or (resource is base and baseOperation is schema)

table ​

Table

  • Type: Remote resource (resourceLocator)
  • Required: Yes
  • Ways to choose: pick from a list, type an ID, paste a URL (airtable.table)
  • Listed inside: base
  • Listed with the connection in: connection
  • Shown when: resource is record or resource is attachment

view ​

View — Optional: restricts the search to the rows visible in that view. ⚠️ Hidden fields are still returned.

  • Type: Remote resource (resourceLocator)
  • Required: No
  • Ways to choose: pick from a list, type an ID, paste a URL (airtable.view)
  • Listed inside: table
  • Listed with the connection in: connection
  • Shown when: resource is record and recordOperation is search

filterMode ​

Filter

  • Type: One choice (options)
  • Required: No
  • Default: conditions
  • Options:
    • conditions — Conditions: Assembled into an Airtable formula, properly escaped.
    • formula — Airtable formula: For what conditions cannot express. Escaping values is then up to you.
    • none — None: Every row (of the chosen view, if any).
  • Shown when: resource is record and recordOperation is search

conditions ​

Conditions — A field is named by its name or its fld… id. Values are escaped: an email subject cannot turn into a formula.

  • Type: List of items (collection)
  • Required: No
  • At most 10 items
  • Each item has:
    • field — Field
      • Type: Text (string)
      • Required: No
      • Default: "" (empty)
      • 200 characters at most
      • Example: Case reference
    • operator — Operator
      • Type: One choice (options)
      • Required: No
      • Default: equals
      • Options:
        • equals — equals
        • notEquals — does not equal
        • contains — contains
        • isEmpty — is empty
        • isNotEmpty — is not empty
        • greaterThan — is greater than
        • lessThan — is less than
        • before — is before the date
        • after — is after the date
        • olderThanDays — is older than (days)
        • newerThanDays — is newer than (days)
    • value — Value. Ignored by “is empty” and “is not empty”. A date reads 2026-09-22.
      • Type: Text (string)
      • Required: No
      • Default: "" (empty)
      • 500 characters at most
  • Shown when: resource is record and recordOperation is search and filterMode is conditions

filterCombine ​

Combination

  • Type: One choice (options)
  • Required: No
  • Default: all
  • Options:
    • all — All conditions
    • any — At least one condition
  • Shown when: resource is record and recordOperation is search and filterMode is conditions

formula ​

Airtable formula — filterByFormula, as Airtable writes it. Escape values coming from an email yourself.

  • Type: Long text (text)
  • Required: No
  • Default: "" (empty)
  • 8000 characters at most
  • Example: {Reference} = "2026-0412"
  • Shown when: resource is record and recordOperation is search and filterMode is formula
  • Expressions: {{ }} accepted

sortField ​

Sort by — Optional. With no sort and no view, the order Airtable returns rows in is arbitrary.

  • Type: Remote resource (resourceLocator)
  • Required: No
  • Ways to choose: pick from a list, type an ID (airtable.field)
  • Listed inside: table
  • Listed with the connection in: connection
  • Shown when: resource is record and recordOperation is search

sortDirection ​

Sort direction

  • Type: One choice (options)
  • Required: No
  • Default: asc
  • Options:
    • asc — Ascending
    • desc — Descending
  • Shown when: resource is record and recordOperation is search

fields ​

Fields to return — Names or ids, comma separated. Empty = all of them. Naming three instead of forty keeps the output and the expressions reading it small.

  • Type: Text (string)
  • Required: No
  • Default: "" (empty)
  • 2000 characters at most
  • Shown when: resource is record and recordOperation is one of search, get
  • Expressions: {{ }} accepted

limit ​

Maximum count

  • Type: Number (number)
  • Required: No
  • Default: 50
  • Whole number, from 1 to 1000
  • Shown when: resource is record and recordOperation is search

recordId ​

Record — The rec… id, as a search returns it in data. Several comma-separated ids are accepted for delete and update.

  • Type: Text (string)
  • Required: No
  • Default: "" (empty)
  • 2000 characters at most
  • Example: {{ data.api_1.recordId }}
  • Shown when: (resource is record and recordOperation is one of get, update, delete, comment) or resource is attachment
  • Expressions: {{ }} accepted

values ​

Fields — The field name (or fld… id) and its value. Values accept {{ }} expressions: {{ email.subject }}.

  • Type: Key / value pairs (keyValue)
  • Required: No
  • Shown when: resource is record and recordOperation is one of create, update, upsert
  • Expressions: {{ }} accepted

fieldsJson ​

Fields as JSON (advanced) — For values that are not text: numbers, checkboxes, lists, links to other tables. An object = one record; an array of objects = several, sent in batches of 10. Merged over the fields above.

  • Type: Long text (text)
  • Required: No
  • Default: "" (empty)
  • 20000 characters at most
  • Example: { "Amount": 1200, "Urgent": true, "Case": ["rec123"] }
  • Shown when: resource is record and recordOperation is one of create, update, upsert
  • Expressions: {{ }} accepted

mergeField ​

Merge field — The business key: case reference, email address, Message-ID. ⚠️ If several rows share the value, Airtable rejects the request — the key must be unique.

  • Type: Remote resource (resourceLocator)
  • Required: Yes
  • Ways to choose: pick from a list, type an ID (airtable.mergeField)
  • Listed inside: table
  • Listed with the connection in: connection
  • Shown when: resource is record and recordOperation is upsert

extraMergeFields ​

Additional merge fields — Optional, two at most (Airtable accepts three in total), comma separated.

  • Type: Text (string)
  • Required: No
  • Default: "" (empty)
  • 400 characters at most
  • Shown when: resource is record and recordOperation is upsert
  • Expressions: {{ }} accepted

typecast ​

Automatically convert values — ⚠️ Airtable then creates missing select options and missing linked records. Needed to map an AI output onto a select, risky on a well-kept base: a typo becomes a new option.

  • Type: Yes / no (boolean)
  • Required: No
  • Default: false
  • Shown when: resource is record and recordOperation is one of create, update, upsert

clearUnspecified ​

Clear unspecified fields — ⚠️ Replaces the whole record (PUT): every field not listed above is cleared. Unchecked, only the given fields change (PATCH).

  • Type: Yes / no (boolean)
  • Required: No
  • Default: false
  • Shown when: resource is record and recordOperation is update

commentText ​

Comment — Accepts {{ }} expressions. @[usrXXXXXXXXXXXXXX] mentions an Airtable collaborator and notifies them.

  • Type: Long text (text)
  • Required: No
  • Default: "" (empty)
  • 10000 characters at most
  • Shown when: resource is record and recordOperation is comment
  • Expressions: {{ }} accepted

attachmentField ​

Attachment field — Only attachment-type fields are offered. Uploading appends: the files already there are kept.

  • Type: Remote resource (resourceLocator)
  • Required: Yes
  • Ways to choose: pick from a list, type an ID (airtable.attachmentField)
  • Listed inside: table
  • Listed with the connection in: connection
  • Shown when: resource is attachment

attachmentSelect ​

Attachments to upload

  • Type: One choice (options)
  • Required: No
  • Default: all
  • Options:
    • all — All of them: Those of the email, then the files added by earlier steps.
    • first — The first one only
    • positions — The ones I name: By their position, as “Read attachments” returns it.
  • Shown when: resource is attachment and attachmentOperation is upload

attachmentPositions ​

Positions — Comma separated: 1,3.

  • Type: Text (string)
  • Required: No
  • Default: "" (empty)
  • 200 characters at most
  • Shown when: resource is attachment and attachmentOperation is upload and attachmentSelect is positions
  • Expressions: {{ }} accepted

includeInline ​

Include inline images — Unchecked, signature logos and other cid: images are skipped — almost always what you want.

  • Type: Yes / no (boolean)
  • Required: No
  • Default: false
  • Shown when: resource is attachment and attachmentOperation is upload

Outputs ​

  • main — Taken once the Airtable operation has succeeded. An error answer from Airtable fails the step instead (except Get a record and the attachment list or fetch, where a missing record gives found: false).

Data produced ​

What this node adds to the run data, and how to read it in an expression. <step> stands for the step key: the node name turned into an identifier (see Data and expressions).

  • {{ data.<step>.records }} — array of { id, createdTime, fields }. Search, Create, Update, Create or update: the records returned by Airtable. id is the rec… id, fields the field values keyed by field name, exactly as Airtable returns them (empty values are omitted by Airtable).
  • {{ data.<step>.recordId }} — string. The rec… id of the first record concerned: the first match (Search), the record read (Get), the first record written (Create, Update, Create or update), or the record targeted (Comment, attachment operations). Empty when there is none.
  • {{ data.<step>.fields }} — object. Search, Get a record, Create or update: the fields of the first record, so that {{ data.<step>.fields.Owner }} works without indexing a list.
  • {{ data.<step>.found }} — boolean. Search: true when at least one row matched. Get a record and the attachment list or fetch: true when the record could be read.
  • {{ data.<step>.count }} — number. The number of items concerned: rows found, records written, records deleted, bases, tables, attachments uploaded, listed or fetched.
  • {{ data.<step>.truncated }} — boolean. Search and List the bases: true when more items existed than the limit allowed.
  • {{ data.<step>.record }} — { id, createdTime, fields } or null. Get a record only: the record read, null when it was not found.
  • {{ data.<step>.recordIds }} — array of string. Create and Update: the ids of the records written (for Update, the ids targeted).
  • {{ data.<step>.warnings }} — array of string. Create, Update, Create or update: the reasons Airtable gives when it accepted the write only partly (for example attachments it could not fetch). Empty in the normal case.
  • {{ data.<step>.cleared }} — boolean. Update only: true when Clear unspecified fields was on, so that the whole record was replaced.
  • {{ data.<step>.created }} — boolean. Create or update only: true when at least one record was created rather than updated. Always false in a test run.
  • {{ data.<step>.createdIds }} — array of string. Create or update only: the ids of the records created.
  • {{ data.<step>.updatedIds }} — array of string. Create or update only: the ids of the existing records updated.
  • {{ data.<step>.createdCount }} — number. Create or update only: the number of records created.
  • {{ data.<step>.updatedCount }} — number. Create or update only: the number of records updated.
  • {{ data.<step>.mergeFields }} — array of string. Create or update only: the merge fields actually used (1 to 3).
  • {{ data.<step>.deletedIds }} — array of string. Delete only: the ids of the records deleted (in a test run, the ids that would have been deleted).
  • {{ data.<step>.commentId }} — string. Comment only: the id of the comment created, empty in a test run.
  • {{ data.<step>.text }} — string. Comment only: the comment text sent.
  • {{ data.<step>.createdTime }} — string. Comment only: the creation time of the comment, empty in a test run.
  • {{ data.<step>.bases }} — array of { id, name, permissionLevel }. List the bases only: the bases the token can reach.
  • {{ data.<step>.baseId }} — string. Get a base schema only: the app… id of the base read.
  • {{ data.<step>.tables }} — array of { id, name, primaryFieldId, fields, views }. Get a base schema only: the tables of the base, each with fields (array of { id, name, type }) and views (array of { id, name, type }).
  • {{ data.<step>.field }} — string. Upload the attachments only: the attachment field written to.
  • {{ data.<step>.uploaded }} — array of { filename, size, type, origin, addedByNodeId? }. Upload the attachments only: the files sent. origin is email (an attachment of the triggering email) or added (a file added by an earlier step, whose node id is in addedByNodeId).
  • {{ data.<step>.skipped }} — array. Upload the attachments: the files left out because they exceed 5 MB, as { filename, size }. Fetch a record's attachments: the files left out, as { field, id, filename, size, type, url, reason } with reason set to too_large or too_many.
  • {{ data.<step>.skippedCount }} — number. Upload the attachments only: the number of files left out.
  • {{ data.<step>.attachments }} — array of { field, id, filename, size, type, url }. List a record's attachments: the attachments of the record. Fetch a record's attachments: the files fetched, with attachmentPosition (their position in the run's attachments) and deduplicated in addition.
  • {{ data.<step>.urlExpiresInMinutes }} — number. List a record's attachments only: how long the url links stay valid, always 120.
  • {{ data.<step>.positions }} — array of number. Fetch a record's attachments only: the positions of the fetched files in the run's attachments, ready for a Compose step.
  • {{ data.<step>.simulated }} — boolean. true when a write was described rather than performed (test run). Reads are never simulated.
  • {{ data.<step>.summary }} — string. A one-line summary in the member's language, for example '1 created, 0 updated.' (with summaryKey and summaryParams).

Operations ​

The node first asks for a Resource (resource), then for the operation of that resource.

Record (resource: record, operation in recordOperation) ​

  • Search (search, the default). Lists the rows of Table (table) that match the filter, up to Maximum count (limit, 1 to 1,000, default 50). Filter (filterMode) is either Conditions (conditions: up to 10 rows of field, operator, value, combined by Combination filterCombine, all or any), an Airtable formula (formula, sent as filterByFormula), or None. View (view) restricts to the rows visible in that view; Sort by (sortField) and Sort direction (sortDirection) order the result; Fields to return (fields, comma-separated) limits the columns. Calls GET /v0/{base}/{table}, or POST …/listRecords with the same parameters when the query becomes long. Publishes records, count, found, recordId, fields, truncated.
  • Get a record (get). Reads the record whose id is in Record (recordId, the first id if several are given), with Fields to return. An unknown record (Airtable answers 404) is not an error: found is false and record is null. Publishes found, record, recordId, fields.
  • Create (create). Adds rows with POST /v0/{base}/{table}. Values come from Fields (values, field name or fld… id and value) and Fields as JSON (advanced) (fieldsJson): a JSON object is one record, an array of objects is several records, sent in batches of 10. Automatically convert values (typecast) lets Airtable convert text into select options and linked records. Publishes records, recordIds, recordId, count, warnings.
  • Create or update (upsert). Sends PATCH /v0/{base}/{table} with performUpsert: Airtable looks for a row whose Merge field (mergeField, plus up to two Additional merge fields extraMergeFields) has the same value, updates it if it exists, creates it otherwise. Same value inputs as Create. Publishes records, recordId, fields, createdIds, updatedIds, createdCount, updatedCount, created, mergeFields, warnings.
  • Update (update). Changes the records whose ids are in Record (several comma-separated ids accepted), or the id carried by each object of the JSON. By default only the given fields change (PATCH). With Clear unspecified fields (clearUnspecified), the whole record is replaced (PUT) and every field not given is emptied. Publishes records, recordIds, recordId, count, cleared, warnings.
  • Delete (delete). Sends the records listed in Record to the base trash (DELETE, batches of 10). Publishes deletedIds, count.
  • Comment (comment). Posts Comment (commentText) on the record in Record. @[usrXXXXXXXXXXXXXX] mentions and notifies a collaborator. Publishes commentId, recordId, text, createdTime.

Attachment (resource: attachment, operation in attachmentOperation) ​

  • Upload the attachments (upload, the default). Sends files of the run to the Attachment field (attachmentField) of the record in Record. Attachments to upload (attachmentSelect) is All of them (the email's attachments, then the files added by earlier steps), The first one only, or The ones I name (Positions attachmentPositions, for example 1,3). Include inline images (includeInline) is off by default. The bytes go straight to content.airtable.com (POST …/uploadAttachment), never through a public URL. Uploading appends: files already in the field are kept. Publishes recordId, field, uploaded, count, skipped, skippedCount.
  • List a record's attachments (list). Reads the record and returns the attachments of the chosen field with their download link. Publishes found, recordId, attachments, count, urlExpiresInMinutes.
  • Fetch a record's attachments into the run (download). Reads the record, then downloads each attachment from Airtable's signed link and adds it to the run's attachments, so that a Compose step can attach it by its position. At most 10 files per step and Maximum size per file (MB) (maxDownloadMb) each. Publishes found, recordId, attachments, positions, count, skipped.

Base (resource: base, operation in baseOperation) ​

  • Get a base schema (schema, the default). Reads the tables of Base, with their fields and views and their ids (GET /v0/meta/bases/{base}/tables). Publishes baseId, tables, count.
  • List the bases (list). Lists the bases the token can reach, with their permission level (GET /v0/meta/bases). Publishes bases, count, truncated.

Every operation also publishes simulated and summary.

Example ​

Each new client request must create one row in a "Cases" table, and only one, even if the email is processed twice. An Extract step named "Extract" has already found the case reference in case_ref. The Airtable node is named "Case record", so its data lives under case_record:

text
resource          record
recordOperation   upsert
base              (list mode) Firm cases
table             (list mode) Cases
mergeField        (list mode) Case_ref
values            Case_ref      → {{ data.extract.case_ref }}
                  Client_email  → {{ email.from.email }}
                  Last_subject  → {{ email.subject }}
typecast          off

For a reference seen for the first time, the step data reads:

json
{
  "records": [
    { "id": "recA1b2C3d4E5f6G7", "createdTime": "2026-10-04T08:12:40.000Z",
      "fields": { "Case_ref": "2026-0412", "Client_email": "marie@example.com", "Last_subject": "New request" } }
  ],
  "recordId": "recA1b2C3d4E5f6G7",
  "fields": { "Case_ref": "2026-0412", "Client_email": "marie@example.com", "Last_subject": "New request" },
  "createdIds": ["recA1b2C3d4E5f6G7"],
  "updatedIds": [],
  "createdCount": 1,
  "updatedCount": 0,
  "created": true,
  "mergeFields": ["fld…"],
  "warnings": [],
  "simulated": false
}

When the next email about case 2026-0412 arrives, the same row is updated: created is false and updatedIds holds its id. A Condition (If) on {{ data.case_record.created }} sends the acknowledgement of receipt only for new cases.

Tips ​

  • Prefer Create or update to Create. Create is not idempotent: if the engine replays the step after an incident, a second row is added. Create or update on a business key (case reference, email address, Message-ID) gives the same row back on every replay. The key must be unique in the table: if several rows share the value, Airtable rejects the request. Delete and Comment are not protected either: a replayed Comment posts the comment twice.
  • Field names in expressions. Expressions only reach keys made of letters, digits, _ and $. A column named Client email cannot be read with {{ data.<step>.fields.Client email }}: name the columns you read in workflows without spaces or accents, or read the whole fields object.
  • Empty values. Airtable leaves empty fields out of its answers ("", [], false), so a field can be missing from fields rather than empty.
  • Conditions are escaped. Values typed in Conditions are escaped before being put into the formula, so an email subject cannot change the filter. With Airtable formula, escaping values that come from an email is up to you. Dates are written 2026-09-22; is older than (days) and is newer than (days) compare with today.
  • Unordered results. Without Sort by and without a view, the order of the rows returned by Airtable is arbitrary.
  • Automatic conversion. With Automatically convert values, Airtable creates missing select options and missing linked records: useful to map an AI output onto a select, risky on a well-kept base, where a typo becomes a new option.
  • Uploads. Airtable accepts at most 5 MB per uploaded file: larger files are skipped and listed in skipped, the others are still sent. Replaying an upload appends the files again.
  • Links that expire. The url returned by List a record's attachments expires after two hours: use it right away, never store it. To keep or forward a file, use Fetch a record's attachments into the run instead.
  • Download size. Maximum size per file (MB) goes from 1 to 10, the server's real limit per downloaded file. A file over the limit fails the step.
  • Get a record. Only an unknown record gives found: false. Any other error answer fails the step with its own code: a refused token with integration.unauthorized (reconnect), a quota or an outage with an automatic retry.
  • Test runs. In test runs, Search, Get a record, the schema and the attachment list and fetch run for real. Create, Create or update, Update, Delete, Comment and Upload are only described: nothing is written, simulated is true, no record id is returned, and Create or update reports created: false.
  • Rate limit. Requests are paced to 4 per second per connection, under Airtable's limit of 5 per second per base. After a 429, Airtable makes you wait 30 seconds: the node honours its Retry-After within four attempts, then the step fails with integration.rate_limited and the engine retries it later.
  • Errors. integration.unauthorized: the token was refused, lacks a scope, or no longer reaches the base; check its scopes and bases at airtable.com/create/tokens. integration.not_found: the base, table or record does not exist or is outside the token's access. integration.rejected: Airtable refused the request (unknown field, value of the wrong type, several rows matching the merge key). node_invalid_param: a required choice is empty, or Fields as JSON is not valid JSON. See Error handling.