{ "kind": "open-flow-workflow", "version": 1, "name": "Import Airtable Image URLs as Attachments", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_08b6f9cb8bdfec42": { "name": "Import image URLs", "kind": "start", "inputDefs": [] }, "n_f790bc314429adcf": { "inputs": { "baseId": { "kind": "value", "value": "YOUR_AIRTABLE_BASE_ID" }, "tableIdOrName": { "kind": "value", "value": "YOUR_AIRTABLE_TABLE_ID" }, "filterByFormula": { "kind": "value", "value": "NOT({Image source URL} = '')" }, "pageSize": { "kind": "value", "value": 100 }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8cdbef4d3887f77b", "output": "offset" } ] } }, "name": "Find records with image URLs", "kind": "task", "task": { "inputs": [ { "description": "Base ID in the format appXXXXXXXXXXXXXX.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Base ID in the format appXXXXXXXXXXXXXX." }, "nullable": false, "handle": "baseId" }, { "description": "Table ID or table name accepted by the Airtable path parameter.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Table ID or table name accepted by the Airtable path parameter." }, "nullable": false, "handle": "tableIdOrName" }, { "description": "View name or view ID used by Airtable to filter and sort results.", "jsonSchema": { "type": "string", "minLength": 1, "description": "View name or view ID used by Airtable to filter and sort results." }, "nullable": true, "handle": "view", "value": null }, { "description": "Field names or field IDs to include in the Airtable response.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "description": "An array item." }, "minItems": 1, "description": "Field names or field IDs to include in the Airtable response." }, "nullable": true, "handle": "fields", "value": null }, { "description": "Sort rules applied by Airtable in order.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string", "minLength": 1, "description": "Field name or field ID used by Airtable for sorting." }, "direction": { "type": "string", "enum": [ "asc", "desc" ], "description": "Sort direction accepted by Airtable." } }, "required": [ "field" ], "additionalProperties": false, "description": "Sort rule accepted by the Airtable list records endpoint." }, "minItems": 1, "description": "Sort rules applied by Airtable in order." }, "nullable": true, "handle": "sort", "value": null }, { "description": "Formula string evaluated by Airtable to filter matching records.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Formula string evaluated by Airtable to filter matching records." }, "nullable": true, "handle": "filterByFormula", "value": null }, { "description": "Maximum total number of records to return before Airtable stops pagination.", "jsonSchema": { "type": "integer", "minimum": 1, "description": "Maximum total number of records to return before Airtable stops pagination." }, "nullable": true, "handle": "maxRecords", "value": null }, { "description": "Number of records to return per page.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Number of records to return per page." }, "nullable": true, "handle": "pageSize", "value": null }, { "description": "Opaque pagination cursor returned by a previous Airtable response.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Opaque pagination cursor returned by a previous Airtable response." }, "nullable": true, "handle": "offset", "value": null }, { "description": "Cell format accepted by Airtable for read operations.", "jsonSchema": { "type": "string", "enum": [ "json", "string" ], "description": "Cell format accepted by Airtable for read operations." }, "nullable": true, "handle": "cellFormat", "value": null }, { "description": "Timezone string sent to Airtable when cellFormat is string.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Timezone string sent to Airtable when cellFormat is string." }, "nullable": true, "handle": "timeZone", "value": null }, { "description": "User locale sent to Airtable when cellFormat is string.", "jsonSchema": { "type": "string", "minLength": 1, "description": "User locale sent to Airtable when cellFormat is string." }, "nullable": true, "handle": "userLocale", "value": null }, { "description": "Whether Airtable should return field IDs instead of field names in record objects.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should return field IDs instead of field names in record objects." }, "nullable": true, "handle": "returnFieldsByFieldId", "value": null }, { "description": "Whether Airtable should return date dependency metadata for linked record fields.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should return date dependency metadata for linked record fields." }, "nullable": true, "handle": "includeDateDependencyMetadata", "value": null }, { "description": "Record metadata fields to include in Airtable list records responses.", "jsonSchema": { "type": "array", "items": { "const": "commentCount", "description": "An array item." }, "minItems": 1, "description": "Record metadata fields to include in Airtable list records responses." }, "nullable": true, "handle": "recordMetadata", "value": null } ], "outputs": [ { "description": "Records returned by Airtable.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable." }, "nullable": false, "handle": "records" }, { "description": "Pagination cursor for the next page of records, or null when unavailable.", "jsonSchema": { "type": [ "string", "null" ], "description": "Pagination cursor for the next page of records, or null when unavailable." }, "nullable": true, "handle": "offset" } ], "name": "Find records with image URLs", "executor": { "kind": "connector", "action": "airtable.list_records" } } }, "n_8cdbef4d3887f77b": { "inputs": { "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f790bc314429adcf", "output": "records" } ] }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f790bc314429adcf", "output": "offset" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8cdbef4d3887f77b", "output": "state" } ] } }, "name": "Collect the next page", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable." }, "nullable": false, "handle": "records" }, { "jsonSchema": { "type": [ "string", "null" ], "description": "Pagination cursor for the next page of records, or null when unavailable." }, "nullable": true, "handle": "offset" }, { "jsonSchema": { "type": "object", "properties": { "records": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable." }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "records", "seen" ], "additionalProperties": false }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "records": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable." }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "records", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect the next page", "moduleId": "n_12edfa448300d7d0", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_ba88fa166dd42311": { "inputs": {}, "name": "More matching records?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_8cdbef4d3887f77b", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_5dcba9f9e0846438": { "inputs": { "pageState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8cdbef4d3887f77b", "output": "state" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5dcba9f9e0846438", "output": "state" } ] } }, "name": "Prepare the next attachment", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": { "records": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable." }, "seen": { "type": "array", "items": { "type": "string" } } }, "required": [ "records", "seen" ], "additionalProperties": false }, "nullable": false, "handle": "pageState" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1, "description": "Record ID in the format recXXXXXXXXXXXXXX." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values to write into the existing Airtable record." } }, "required": [ "id", "fields" ], "additionalProperties": false, "description": "Record payload used when updating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to update in the Airtable table." }, "nullable": false, "handle": "records" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" } ], "name": "Prepare the next attachment", "moduleId": "n_58e5550cf638b5fa", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_15099a7a29c17d84": { "inputs": {}, "name": "More attachments to update?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_5dcba9f9e0846438", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_ae6e64bb60b00f61": { "inputs": { "baseId": { "kind": "value", "value": "YOUR_AIRTABLE_BASE_ID" }, "tableIdOrName": { "kind": "value", "value": "YOUR_AIRTABLE_TABLE_ID" }, "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5dcba9f9e0846438", "output": "records" } ] }, "typecast": { "kind": "value", "value": false } }, "name": "Update the matching record", "kind": "task", "task": { "inputs": [ { "description": "Base ID in the format appXXXXXXXXXXXXXX.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Base ID in the format appXXXXXXXXXXXXXX." }, "nullable": false, "handle": "baseId" }, { "description": "Table ID or table name accepted by the Airtable path parameter.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Table ID or table name accepted by the Airtable path parameter." }, "nullable": false, "handle": "tableIdOrName" }, { "description": "Records to update in the Airtable table.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1, "description": "Record ID in the format recXXXXXXXXXXXXXX." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values to write into the existing Airtable record." } }, "required": [ "id", "fields" ], "additionalProperties": false, "description": "Record payload used when updating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to update in the Airtable table." }, "nullable": false, "handle": "records" }, { "description": "Whether Airtable should coerce incoming values to compatible field types.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should coerce incoming values to compatible field types." }, "nullable": true, "handle": "typecast", "value": null }, { "description": "Whether Airtable should return field IDs instead of field names in record objects.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should return field IDs instead of field names in record objects." }, "nullable": true, "handle": "returnFieldsByFieldId", "value": null } ], "outputs": [ { "description": "Records returned by Airtable after update.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable after update." }, "nullable": false, "handle": "records" } ], "name": "Update the matching record", "executor": { "kind": "connector", "action": "airtable.update_records" } } }, "n_6366fad578299ff1": { "name": "Updated record count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5dcba9f9e0846438", "output": "count" } ] } } } }, "edges": [ { "source": "n_08b6f9cb8bdfec42", "target": "n_f790bc314429adcf" }, { "source": "n_f790bc314429adcf", "target": "n_8cdbef4d3887f77b" }, { "source": "n_8cdbef4d3887f77b", "target": "n_ba88fa166dd42311" }, { "source": "n_ba88fa166dd42311", "target": "n_f790bc314429adcf", "sourceHandle": "more" }, { "source": "n_ba88fa166dd42311", "target": "n_5dcba9f9e0846438", "sourceHandle": "otherwise" }, { "source": "n_5dcba9f9e0846438", "target": "n_15099a7a29c17d84" }, { "source": "n_15099a7a29c17d84", "target": "n_ae6e64bb60b00f61", "sourceHandle": "more" }, { "source": "n_ae6e64bb60b00f61", "target": "n_5dcba9f9e0846438" }, { "source": "n_15099a7a29c17d84", "target": "n_6366fad578299ff1", "sourceHandle": "otherwise" } ] } }, "modules": { "n_12edfa448300d7d0": { "name": "Collect the next page", "imports": [], "source": "export default function collectImagePages({records,offset,previousState}) {\n if (!Array.isArray(records)) throw new Error('Missing Airtable records');\n const state=previousState ?? {records:[],seen:[]};\n if (!Array.isArray(state.records)||!Array.isArray(state.seen)) throw new Error('Invalid pagination state');\n if (offset!==null && (typeof offset!=='string'||!offset||state.seen.includes(offset))) throw new Error('Invalid or repeated Airtable offset');\n return {state:{records:[...state.records,...records],seen:offset===null?state.seen:[...state.seen,offset]},offset:offset??\"unused\",more:offset!==null};\n}\n" }, "n_58e5550cf638b5fa": { "name": "Prepare the next attachment", "imports": [], "source": "export default function nextImageUpdate({pageState,previousState}) {\n const index=previousState?.index ?? 0, records=pageState.records;\n if (!Array.isArray(records)||!Number.isInteger(index)||index<0||index>records.length) throw new Error('Invalid update state');\n if(index===records.length) return {state:{index},more:false,records:[{id:\"unused\",fields:{}}],count:index};\n const record=records[index],url=record?.fields?.['Image source URL'];\n if(typeof record?.id!=='string'||!record.id||typeof url!=='string'||!url.trim())throw new Error('Missing record ID or image URL');\n return {state:{index:index+1},more:true,records:[{id:record.id,fields:{'Image attachment':[{url}]}}],count:index+1};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }