{ "kind": "open-flow-workflow", "version": 1, "name": "Sheet Contacts through Dropcontact to Lemlist", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_a9fc1b8fadb06180": { "name": "Import the sheet contacts", "kind": "start", "inputDefs": [] }, "n_20ece4424693e242": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "A:K" }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" }, "dateTimeRenderOption": { "kind": "value", "value": "SERIAL_NUMBER" } }, "name": "Read every configured contact row", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The A1 notation of the range to retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read every configured contact row", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_2fe7c7e9870b0ee3": { "inputs": { "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_20ece4424693e242", "output": "values" } ] } }, "name": "Read the spreadsheet column labels", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "hasContacts" } ], "name": "Read the spreadsheet column labels", "moduleId": "n_c509428e2a448c23", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_54b361565c12cf70": { "inputs": { "rows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "rows" } ] } }, "name": "Map the complete contact batch", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "email": { "type": "string", "minLength": 1, "description": "The email address to verify or enrich." }, "first_name": { "type": "string", "minLength": 1, "description": "The contact's first name." }, "last_name": { "type": "string", "minLength": 1, "description": "The contact's last name." }, "full_name": { "type": "string", "minLength": 1, "description": "The contact's full name." }, "phone": { "type": "string", "minLength": 1, "description": "The contact's phone number." }, "company": { "type": "string", "minLength": 1, "description": "The contact's company name." }, "website": { "type": "string", "minLength": 1, "description": "The company website or domain." }, "num_siren": { "type": "string", "minLength": 1, "description": "The French company SIREN number." }, "linkedin": { "type": "string", "minLength": 1, "description": "The contact's LinkedIn URL or identifier." }, "siret": { "type": "string", "minLength": 1, "description": "The French company SIRET number." }, "country": { "type": "string", "minLength": 1, "description": "The country code used to guide enrichment." }, "job": { "type": "string", "minLength": 1, "description": "The contact's job title." }, "company_linkedin": { "type": "string", "minLength": 1, "description": "The company's LinkedIn URL or identifier." }, "custom_fields": { "type": "object", "additionalProperties": { "type": "string", "description": "One custom field value." }, "description": "Custom string fields that Dropcontact returns unchanged." } }, "additionalProperties": false, "description": "One contact or organization record for Dropcontact to enrich." }, "minItems": 1, "maxItems": 250, "description": "Contacts to enrich in one Dropcontact batch." }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "hasContacts" } ], "name": "Map the complete contact batch", "moduleId": "n_dc44cec0f00e18b0", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_6fbf49668867817e": { "inputs": {}, "name": "Any contacts to enrich", "kind": "condition", "matchMode": "first", "cases": [ { "output": "contacts", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "hasContacts" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_6f345933b6c878e1": { "inputs": { "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_54b361565c12cf70", "output": "data" } ] }, "siren": { "kind": "value", "value": true } }, "name": "Submit one French company enrichment", "kind": "task", "task": { "inputs": [ { "description": "Contacts to enrich in one Dropcontact batch.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "email": { "type": "string", "minLength": 1, "description": "The email address to verify or enrich." }, "first_name": { "type": "string", "minLength": 1, "description": "The contact's first name." }, "last_name": { "type": "string", "minLength": 1, "description": "The contact's last name." }, "full_name": { "type": "string", "minLength": 1, "description": "The contact's full name." }, "phone": { "type": "string", "minLength": 1, "description": "The contact's phone number." }, "company": { "type": "string", "minLength": 1, "description": "The contact's company name." }, "website": { "type": "string", "minLength": 1, "description": "The company website or domain." }, "num_siren": { "type": "string", "minLength": 1, "description": "The French company SIREN number." }, "linkedin": { "type": "string", "minLength": 1, "description": "The contact's LinkedIn URL or identifier." }, "siret": { "type": "string", "minLength": 1, "description": "The French company SIRET number." }, "country": { "type": "string", "minLength": 1, "description": "The country code used to guide enrichment." }, "job": { "type": "string", "minLength": 1, "description": "The contact's job title." }, "company_linkedin": { "type": "string", "minLength": 1, "description": "The company's LinkedIn URL or identifier." }, "custom_fields": { "type": "object", "additionalProperties": { "type": "string", "description": "One custom field value." }, "description": "Custom string fields that Dropcontact returns unchanged." } }, "additionalProperties": false, "description": "One contact or organization record for Dropcontact to enrich." }, "minItems": 1, "maxItems": 250, "description": "Contacts to enrich in one Dropcontact batch." }, "nullable": false, "handle": "data" }, { "description": "Whether to request French company identifiers, address data, and company-leader information.", "jsonSchema": { "type": "boolean", "description": "Whether to request French company identifiers, address data, and company-leader information." }, "nullable": true, "handle": "siren", "value": null }, { "description": "The language for enriched data. Dropcontact documents English.", "jsonSchema": { "type": "string", "enum": [ "en" ], "description": "The language for enriched data. Dropcontact documents English." }, "nullable": true, "handle": "language", "value": null } ], "outputs": [ { "description": "Whether Dropcontact reported an error for the submission.", "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact reported an error for the submission." }, "nullable": false, "handle": "error" }, { "description": "The text returned by Dropcontact when present.", "jsonSchema": { "type": [ "string", "null" ], "description": "The text returned by Dropcontact when present." }, "nullable": true, "handle": "request_id" }, { "description": "Whether Dropcontact accepted the enrichment batch.", "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact accepted the enrichment batch." }, "nullable": false, "handle": "success" }, { "description": "The integer value returned by Dropcontact when present.", "jsonSchema": { "type": [ "integer", "null" ], "description": "The integer value returned by Dropcontact when present." }, "nullable": true, "handle": "credits_left" }, { "description": "A raw object returned by Dropcontact.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "nullable": false, "handle": "raw" } ], "name": "Submit one French company enrichment", "executor": { "kind": "connector", "action": "dropcontact.submit_enrichment" } } }, "n_f1a1131d02f0d477": { "inputs": { "error": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6f345933b6c878e1", "output": "error" } ] }, "success": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6f345933b6c878e1", "output": "success" } ] }, "requestId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6f345933b6c878e1", "output": "request_id" } ] }, "raw": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6f345933b6c878e1", "output": "raw" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "count" } ] } }, "name": "Keep the accepted request ID", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact reported an error for the submission." }, "nullable": false, "handle": "error" }, { "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact accepted the enrichment batch." }, "nullable": false, "handle": "success" }, { "jsonSchema": { "type": [ "string", "null" ], "description": "The text returned by Dropcontact when present." }, "nullable": true, "handle": "requestId" }, { "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "nullable": false, "handle": "raw" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "requestId" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "submission" } ], "name": "Keep the accepted request ID", "moduleId": "n_b2dea763c1013a55", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_568dae6aad8447a9": { "inputs": {}, "name": "Wait 45 seconds for enrichment", "kind": "wait", "inputDefinitions": [], "prompt": "Wait 45 seconds for enrichment", "autoContinue": { "kind": "after", "durationMs": 45000 } }, "n_19eed8ca499c2818": { "inputs": { "request_id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f1a1131d02f0d477", "output": "requestId" } ] }, "forceResults": { "kind": "value", "value": false } }, "name": "Fetch the one enrichment result", "kind": "task", "task": { "inputs": [ { "description": "The request ID returned by submit_enrichment.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The request ID returned by submit_enrichment." }, "nullable": false, "handle": "request_id" }, { "description": "Whether to return processed records immediately while unprocessed records remain unchanged.", "jsonSchema": { "type": "boolean", "description": "Whether to return processed records immediately while unprocessed records remain unchanged." }, "nullable": true, "handle": "forceResults", "value": null } ], "outputs": [ { "description": "Whether Dropcontact reported an error for the result request.", "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact reported an error for the result request." }, "nullable": false, "handle": "error" }, { "description": "Whether Dropcontact has completed the enrichment request.", "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact has completed the enrichment request." }, "nullable": false, "handle": "success" }, { "description": "The connector lifecycle state derived from the Dropcontact response.", "jsonSchema": { "type": "string", "enum": [ "running", "succeeded", "failed" ], "description": "The connector lifecycle state derived from the Dropcontact response." }, "nullable": false, "handle": "status" }, { "description": "The text returned by Dropcontact when present.", "jsonSchema": { "type": [ "string", "null" ], "description": "The text returned by Dropcontact when present." }, "nullable": true, "handle": "reason" }, { "description": "The integer value returned by Dropcontact when present.", "jsonSchema": { "type": [ "integer", "null" ], "description": "The integer value returned by Dropcontact when present." }, "nullable": true, "handle": "credits_left" }, { "description": "Contacts returned by Dropcontact.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "description": "Contacts returned by Dropcontact." }, "nullable": false, "handle": "data" }, { "description": "A raw object returned by Dropcontact.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "nullable": false, "handle": "raw" } ], "name": "Fetch the one enrichment result", "executor": { "kind": "connector", "action": "dropcontact.get_enrichment_result" } } }, "n_2ed8927064b2d944": { "inputs": { "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19eed8ca499c2818", "output": "data" } ] }, "error": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19eed8ca499c2818", "output": "error" } ] }, "success": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19eed8ca499c2818", "output": "success" } ] }, "status": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19eed8ca499c2818", "output": "status" } ] }, "raw": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19eed8ca499c2818", "output": "raw" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "count" } ] }, "campaignId": { "kind": "value", "value": "YOUR_LEMLIST_CAMPAIGN_ID" } }, "name": "Validate every enriched lead", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "description": "Contacts returned by Dropcontact." }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact reported an error for the result request." }, "nullable": false, "handle": "error" }, { "jsonSchema": { "type": "boolean", "description": "Whether Dropcontact has completed the enrichment request." }, "nullable": false, "handle": "success" }, { "jsonSchema": { "type": "string", "enum": [ "running", "succeeded", "failed" ], "description": "The connector lifecycle state derived from the Dropcontact response." }, "nullable": false, "handle": "status" }, { "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A raw object returned by Dropcontact." }, "nullable": false, "handle": "raw" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "campaignId" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "plans" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "enriched" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" } ], "name": "Validate every enriched lead", "moduleId": "n_e369b7dc221a34af", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f7af89a36f66fa24": { "inputs": { "plans": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2ed8927064b2d944", "output": "plans" } ] }, "expectedCount": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "count" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7af89a36f66fa24", "output": "state" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_805e76224afde232", "output": "statusCode" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_805e76224afde232", "output": "body" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_805e76224afde232", "output": "bodyEncoding" } ] } }, "name": "Prepare the next campaign lead", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": true, "handle": "plans" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "expectedCount" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "integer" }, "nullable": true, "handle": "statusCode" }, { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" } ], "name": "Prepare the next campaign lead", "moduleId": "n_05d7e558a7ee0da1", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_670e77385c9727f3": { "inputs": {}, "name": "More leads to create", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_f7af89a36f66fa24", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_805e76224afde232": { "inputs": { "endpoint": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7af89a36f66fa24", "output": "endpoint" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7af89a36f66fa24", "output": "body" } ] } }, "name": "Create the Lemlist campaign lead", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Create the Lemlist campaign lead", "executor": { "providerId": "lemlist", "method": "POST", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_fcfe31ff23e4cff2": { "inputs": { "state": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7af89a36f66fa24", "output": "state" } ] }, "enriched": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2ed8927064b2d944", "output": "enriched" } ] }, "submission": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f1a1131d02f0d477", "output": "submission" } ] }, "expectedCount": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2fe7c7e9870b0ee3", "output": "count" } ] } }, "name": "Confirm the entire contact import", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": true, "handle": "enriched" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "submission" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "expectedCount" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "leads" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "enriched" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "submission" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" } ], "name": "Confirm the entire contact import", "moduleId": "n_4eb14b07a4971a2f", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_58aa7cbfda3abc5c": { "name": "Enriched contacts and saved leads", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "leads" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "enriched" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "submission" }, { "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 250 }, "nullable": false, "handle": "count" } ], "outputMappings": { "leads": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcfe31ff23e4cff2", "output": "leads" } ] }, "enriched": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcfe31ff23e4cff2", "output": "enriched" } ] }, "submission": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcfe31ff23e4cff2", "output": "submission" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcfe31ff23e4cff2", "output": "count" } ] } } } }, "edges": [ { "source": "n_a9fc1b8fadb06180", "target": "n_20ece4424693e242" }, { "source": "n_20ece4424693e242", "target": "n_2fe7c7e9870b0ee3" }, { "source": "n_2fe7c7e9870b0ee3", "target": "n_6fbf49668867817e" }, { "source": "n_6fbf49668867817e", "target": "n_54b361565c12cf70", "sourceHandle": "contacts" }, { "source": "n_54b361565c12cf70", "target": "n_6f345933b6c878e1" }, { "source": "n_6fbf49668867817e", "target": "n_f7af89a36f66fa24", "sourceHandle": "otherwise" }, { "source": "n_6f345933b6c878e1", "target": "n_f1a1131d02f0d477" }, { "source": "n_f1a1131d02f0d477", "target": "n_568dae6aad8447a9" }, { "source": "n_568dae6aad8447a9", "target": "n_19eed8ca499c2818", "sourceHandle": "continue" }, { "source": "n_19eed8ca499c2818", "target": "n_2ed8927064b2d944" }, { "source": "n_2ed8927064b2d944", "target": "n_f7af89a36f66fa24" }, { "source": "n_f7af89a36f66fa24", "target": "n_670e77385c9727f3" }, { "source": "n_670e77385c9727f3", "target": "n_805e76224afde232", "sourceHandle": "more" }, { "source": "n_805e76224afde232", "target": "n_f7af89a36f66fa24" }, { "source": "n_670e77385c9727f3", "target": "n_fcfe31ff23e4cff2", "sourceHandle": "otherwise" }, { "source": "n_fcfe31ff23e4cff2", "target": "n_58aa7cbfda3abc5c" } ] } }, "modules": { "n_c509428e2a448c23": { "name": "Read the spreadsheet column labels", "imports": [], "source": "function sheetTableRows({ values }) {\n if (!Array.isArray(values) || values.some(row => !Array.isArray(row))) throw new Error('Expected the sheet range as rows of cell values');\n for (const row of values) for (const cell of row) if (cell !== null && typeof cell !== 'string' && typeof cell !== 'boolean' && !(typeof cell === 'number' && Number.isFinite(cell))) throw new Error('The sheet returned an invalid cell value');\n if (!values.length) return { rows: [] };\n const headers = values[0], rows = [];\n for (const cells of values.slice(1)) {\n const row = Object.create(null);\n for (let column = 0; column < cells.length; column++) {\n const header = headers[column];\n if (header) row[String(header)] = cells[column];\n }\n if (Object.keys(row).length) rows.push(row);\n }\n return { rows };\n}\n\nexport default function(input){const {rows}=sheetTableRows(input);if(rows.length>250)throw new Error(\"Choose a range containing at most250contacts for one enrichment batch\");return {rows,count:rows.length,hasContacts:rows.length>0};}" }, "n_dc44cec0f00e18b0": { "name": "Map the complete contact batch", "imports": [], "source": "export default function sheetDropcontactContacts({ rows }) {\n if (!Array.isArray(rows) || rows.length === 0) throw new Error('Expected the complete spreadsheet rows');\n if (rows.length > 250) throw new Error('The source submits one enrichment batch; choose a range containing at most 250 contacts before running');\n const fields = { email: 'email', firstName: 'first_name', lastName: 'last_name', fullName: 'full_name', companyName: 'company', website: 'website', LinkedIn: 'linkedin' };\n const data = rows.map((row, index) => {\n if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error('Invalid spreadsheet contact at row ' + (index + 2));\n const contact = {};\n for (const [column, field] of Object.entries(fields)) {\n const value = row[column];\n if (value === undefined || value === null || value === '') continue;\n if (typeof value !== 'string') throw new Error('Contact column ' + column + ' must contain text at row ' + (index + 2));\n if (value.trim()) contact[field] = value;\n }\n if (!(contact.email || contact.linkedin || ((contact.company || contact.website) && ((contact.first_name && contact.last_name) || contact.full_name)))) throw new Error('Contact row ' + (index + 2) + ' needs email, LinkedIn, or a name and company/website');\n return contact;\n });\n return { data, count: data.length, hasContacts: data.length > 0 };\n}\n" }, "n_b2dea763c1013a55": { "name": "Keep the accepted request ID", "imports": [], "source": "export default function dropcontactSubmission({ error, success, requestId, raw, count }) {\n if (error !== false || success !== true || typeof requestId !== 'string' || !requestId.trim() || !raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('Dropcontact did not confirm the enrichment request; submission may already have occurred');\n if (!Number.isInteger(count) || count < 1 || count > 250) throw new Error('Invalid enrichment contact count');\n return { requestId, count, submission: raw };\n}\n" }, "n_e369b7dc221a34af": { "name": "Validate every enriched lead", "imports": [], "source": "export default function dropcontactLeadPlans({ data, error, success, status, raw, count, campaignId }) {\n if (error !== false || success !== true || status !== 'succeeded') throw new Error('Dropcontact enrichment is not complete after the source wait; do not submit the batch again without checking its request ID');\n if (!Array.isArray(data) || data.length !== count) throw new Error('Dropcontact did not return the complete contact batch');\n if (raw !== null && raw !== undefined && (!Array.isArray(raw.data) || raw.data.length !== count || raw.data.some(record => !record || typeof record !== 'object' || Array.isArray(record)))) throw new Error('Dropcontact returned an invalid or incomplete raw contact batch');\n if (typeof campaignId !== 'string' || !campaignId.trim()) throw new Error('Configure the Lemlist campaign ID');\n const campaignPath = encodeURIComponent(campaignId);\n const plans = data.map((record, index) => {\n if (!record || typeof record !== 'object' || Array.isArray(record) || !Array.isArray(record.email) || !record.email.length || typeof record.email[0]?.email !== 'string' || !record.email[0].email.trim()) throw new Error('Contact ' + (index + 1) + ' has no first enriched email; no leads have been created by this step');\n const email = record.email[0].email;\n const body = {};\n for (const [target, source] of [['firstName', 'first_name'], ['lastName', 'last_name'], ['companyName', 'company']]) {\n if (record[source] === undefined) continue;\n if (record[source] !== null && typeof record[source] !== 'string') throw new Error('Invalid enriched field ' + source + ' at contact ' + (index + 1));\n body[target] = record[source];\n }\n return { email, emailPath: encodeURIComponent(email), campaignPath, endpoint: '/campaigns/' + campaignPath + '/leads/' + encodeURIComponent(email), body };\n });\n return { plans, enriched: data, count };\n}\n" }, "n_05d7e558a7ee0da1": { "name": "Prepare the next campaign lead", "imports": [], "source": "export default function lemlistLeadProgress({ plans, expectedCount, previousState, statusCode, body, bodyEncoding }) {\n if (expectedCount === 0 && (plans === null || plans === undefined)) plans = [];\n if (!Array.isArray(plans) || plans.length !== expectedCount || plans.length > 250) throw new Error('Expected the complete Lemlist lead plan');\n let state;\n if (previousState === null || previousState === undefined) state = { index: 0, leads: [] };\n else {\n if (!Number.isInteger(previousState.index) || previousState.index < 0 || previousState.index >= plans.length || !Array.isArray(previousState.leads) || previousState.leads.length !== previousState.index) throw new Error('Invalid Lemlist lead progress');\n if (!Number.isInteger(statusCode) || statusCode < 200 || statusCode >= 300 || bodyEncoding !== null && bodyEncoding !== undefined || !body || typeof body !== 'object' || Array.isArray(body) || typeof body._id !== 'string' || !body._id.trim()) throw new Error('Lemlist did not confirm the created lead; this or earlier leads may already exist');\n if (body.email !== undefined && (typeof body.email !== 'string' || body.email.toLowerCase() !== plans[previousState.index].email.toLowerCase())) throw new Error('Lemlist returned a different lead email; creation may already have occurred');\n state = { index: previousState.index + 1, leads: previousState.leads.concat([body]) };\n }\n const more = state.index < plans.length;\n return { state, more, endpoint: more ? plans[state.index].endpoint : '', body: more ? plans[state.index].body : {}, count: state.leads.length };\n}\n" }, "n_4eb14b07a4971a2f": { "name": "Confirm the entire contact import", "imports": [], "source": "export default function sheetLemlistResult({ state, enriched, submission, expectedCount }) {\n if (!state || !Array.isArray(state.leads) || state.leads.length !== expectedCount || state.index !== expectedCount) throw new Error('The complete sheet contact batch has not been confirmed in Lemlist');\n if (expectedCount === 0) return { leads: [], enriched: [], submission: null, count: 0 };\n if (!Array.isArray(enriched) || enriched.length !== expectedCount || !submission || typeof submission !== 'object') throw new Error('Missing the complete enrichment/submission result');\n return { leads: state.leads, enriched, submission, count: state.leads.length };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }