{ "kind": "open-flow-workflow", "version": 1, "name": "Notify Invoice Emails in Slack", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_f5451b5322674882": { "name": "Check inbox every minute", "kind": "cron", "cronTimes": [ { "type": "every", "unit": "minute", "value": 1 } ] }, "n_9b680e4e27d17c86": { "inputs": { "folder": { "kind": "value", "value": "INBOX" }, "unseen": { "kind": "value", "value": true }, "limit": { "kind": "value", "value": 1 } }, "name": "Find an unread message", "kind": "task", "task": { "inputs": [ { "description": "The IMAP folder path returned by list_folders.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path returned by list_folders.", "default": "INBOX" }, "nullable": true, "handle": "folder", "value": "INBOX" }, { "description": "Whether to return only unread messages.", "jsonSchema": { "type": "boolean", "description": "Whether to return only unread messages." }, "nullable": true, "handle": "unseen", "value": null }, { "description": "A sender search term.", "jsonSchema": { "type": "string", "minLength": 1, "description": "A sender search term." }, "nullable": true, "handle": "from", "value": null }, { "description": "A recipient search term.", "jsonSchema": { "type": "string", "minLength": 1, "description": "A recipient search term." }, "nullable": true, "handle": "to", "value": null }, { "description": "A subject search term.", "jsonSchema": { "type": "string", "minLength": 1, "description": "A subject search term." }, "nullable": true, "handle": "subject", "value": null }, { "description": "A body text search term.", "jsonSchema": { "type": "string", "minLength": 1, "description": "A body text search term." }, "nullable": true, "handle": "text", "value": null }, { "description": "Only include messages on or after this YYYY-MM-DD date.", "jsonSchema": { "type": "string", "format": "date", "description": "Only include messages on or after this YYYY-MM-DD date." }, "nullable": true, "handle": "since", "value": null }, { "description": "Only include messages before this YYYY-MM-DD date.", "jsonSchema": { "type": "string", "format": "date", "description": "Only include messages before this YYYY-MM-DD date." }, "nullable": true, "handle": "before", "value": null }, { "description": "The maximum number of email summaries to return.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "The maximum number of email summaries to return.", "default": 20 }, "nullable": true, "handle": "limit", "value": 20 }, { "description": "Only include message UIDs lower than this cursor.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "Only include message UIDs lower than this cursor." }, "nullable": true, "handle": "beforeUid", "value": null } ], "outputs": [ { "description": "The IMAP folder path that was searched.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path that was searched." }, "nullable": false, "handle": "folder" }, { "description": "The email summaries returned for this page.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "uid": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "messageId": { "type": [ "string", "null" ], "description": "The Message-ID header when available." }, "subject": { "type": [ "string", "null" ], "description": "The email subject when available." }, "from": { "type": [ "object", "null" ], "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "to": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "description": "The primary recipients parsed from the message envelope." }, "date": { "type": [ "string", "null" ], "format": "date-time", "description": "The message date as an ISO 8601 timestamp." }, "flags": { "type": "array", "items": { "type": "string", "description": "One IMAP flag." }, "description": "The IMAP flags currently set on the message." }, "seen": { "type": "boolean", "description": "Whether the message has the IMAP Seen flag." }, "hasAttachments": { "type": "boolean", "description": "Whether the message has attachment parts." }, "size": { "type": [ "integer", "null" ], "description": "The message size in bytes when available." } }, "required": [ "uid", "messageId", "subject", "from", "to", "date", "flags", "seen", "hasAttachments", "size" ], "additionalProperties": false, "description": "A lightweight IMAP Mailbox message summary." }, "description": "The email summaries returned for this page." }, "nullable": false, "handle": "emails" }, { "description": "The cursor to pass as beforeUid for the next page.", "jsonSchema": { "type": [ "integer", "null" ], "exclusiveMinimum": 0, "description": "The cursor to pass as beforeUid for the next page." }, "nullable": true, "handle": "nextBeforeUid" } ], "name": "Find an unread message", "executor": { "kind": "connector", "action": "generic_imap.search_emails" } } }, "n_bf2d961c61c50c9d": { "inputs": { "emails": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9b680e4e27d17c86", "output": "emails" } ] } }, "name": "Select the message", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "uid": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "messageId": { "type": [ "string", "null" ], "description": "The Message-ID header when available." }, "subject": { "type": [ "string", "null" ], "description": "The email subject when available." }, "from": { "type": [ "object", "null" ], "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "to": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "description": "The primary recipients parsed from the message envelope." }, "date": { "type": [ "string", "null" ], "format": "date-time", "description": "The message date as an ISO 8601 timestamp." }, "flags": { "type": "array", "items": { "type": "string", "description": "One IMAP flag." }, "description": "The IMAP flags currently set on the message." }, "seen": { "type": "boolean", "description": "Whether the message has the IMAP Seen flag." }, "hasAttachments": { "type": "boolean", "description": "Whether the message has attachment parts." }, "size": { "type": [ "integer", "null" ], "description": "The message size in bytes when available." } }, "required": [ "uid", "messageId", "subject", "from", "to", "date", "flags", "seen", "hasAttachments", "size" ], "additionalProperties": false, "description": "A lightweight IMAP Mailbox message summary." }, "description": "The email summaries returned for this page." }, "nullable": false, "handle": "emails" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" } ], "name": "Select the message", "moduleId": "n_0b8253bde36637a2", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_c4578d179ae78905": { "inputs": {}, "name": "Unread message found?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_0dea1ee79591c4e8": { "inputs": { "folder": { "kind": "value", "value": "INBOX" }, "uid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "uid" } ] } }, "name": "Read the message", "kind": "task", "task": { "inputs": [ { "description": "The IMAP folder path returned by list_folders.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path returned by list_folders.", "default": "INBOX" }, "nullable": true, "handle": "folder", "value": "INBOX" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" } ], "outputs": [ { "description": "The IMAP folder path that contained the message.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path that contained the message." }, "nullable": false, "handle": "folder" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "description": "The Message-ID header when available.", "jsonSchema": { "type": [ "string", "null" ], "description": "The Message-ID header when available." }, "nullable": true, "handle": "messageId" }, { "description": "The email subject when available.", "jsonSchema": { "type": [ "string", "null" ], "description": "The email subject when available." }, "nullable": true, "handle": "subject" }, { "description": "A normalized email address.", "jsonSchema": { "type": [ "object", "null" ], "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "nullable": true, "handle": "from" }, { "description": "The primary recipients parsed from the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "description": "The primary recipients parsed from the message." }, "nullable": false, "handle": "to" }, { "description": "The carbon-copy recipients parsed from the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "description": "The carbon-copy recipients parsed from the message." }, "nullable": false, "handle": "cc" }, { "description": "The message date as an ISO 8601 timestamp.", "jsonSchema": { "type": [ "string", "null" ], "format": "date-time", "description": "The message date as an ISO 8601 timestamp." }, "nullable": true, "handle": "date" }, { "description": "The IMAP flags currently set on the message.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "One IMAP flag." }, "description": "The IMAP flags currently set on the message." }, "nullable": false, "handle": "flags" }, { "description": "Whether the message has the IMAP Seen flag.", "jsonSchema": { "type": "boolean", "description": "Whether the message has the IMAP Seen flag." }, "nullable": false, "handle": "seen" }, { "description": "The parsed plain text body within the fetch budget.", "jsonSchema": { "type": [ "string", "null" ], "description": "The parsed plain text body within the fetch budget." }, "nullable": true, "handle": "text" }, { "description": "The parsed HTML body within the fetch budget.", "jsonSchema": { "type": [ "string", "null" ], "description": "The parsed HTML body within the fetch budget." }, "nullable": true, "handle": "html" }, { "description": "Whether returned body content was truncated or omitted by budget.", "jsonSchema": { "type": "boolean", "description": "Whether returned body content was truncated or omitted by budget." }, "nullable": false, "handle": "truncated" }, { "description": "Attachment metadata parsed from the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "attachmentId": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "filename": { "type": [ "string", "null" ], "description": "The attachment filename when available." }, "contentType": { "type": [ "string", "null" ], "description": "The attachment MIME content type when available." }, "size": { "type": [ "integer", "null" ], "description": "The attachment size in bytes when available." }, "contentId": { "type": [ "string", "null" ], "description": "The attachment Content-ID when available." } }, "required": [ "attachmentId", "filename", "contentType", "size", "contentId" ], "additionalProperties": false, "description": "Attachment metadata without attachment content." }, "description": "Attachment metadata parsed from the message." }, "nullable": false, "handle": "attachments" } ], "name": "Read the message", "executor": { "kind": "connector", "action": "generic_imap.get_email" } } }, "n_fa5754b668f7d931": { "inputs": { "folder": { "kind": "value", "value": "INBOX" }, "uid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "uid" } ] } }, "name": "Mark message read", "kind": "task", "task": { "inputs": [ { "description": "The IMAP folder path returned by list_folders.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path returned by list_folders.", "default": "INBOX" }, "nullable": true, "handle": "folder", "value": "INBOX" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" } ], "outputs": [ { "description": "The IMAP folder path that contained the message.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path that contained the message." }, "nullable": false, "handle": "folder" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "description": "Whether the message is now marked as read.", "jsonSchema": { "const": true, "description": "Whether the message is now marked as read." }, "nullable": false, "handle": "read" } ], "name": "Mark message read", "executor": { "kind": "connector", "action": "generic_imap.mark_email_read" } } }, "n_8a134dc3b8043e80": { "inputs": { "uid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "uid" } ] }, "actualUid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "uid" } ] }, "text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "text" } ] }, "truncated": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "truncated" } ] }, "from": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "from" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "subject" } ] }, "date": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "date" } ] }, "attachments": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0dea1ee79591c4e8", "output": "attachments" } ] } }, "name": "Check the message body", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "actualUid" }, { "jsonSchema": { "type": [ "string", "null" ], "description": "The parsed plain text body within the fetch budget." }, "nullable": true, "handle": "text" }, { "jsonSchema": { "type": "boolean", "description": "Whether returned body content was truncated or omitted by budget." }, "nullable": false, "handle": "truncated" }, { "jsonSchema": { "type": [ "object", "null" ], "properties": { "name": { "type": [ "string", "null" ], "description": "The display name when one was provided." }, "email": { "type": [ "string", "null" ], "description": "The email address when one was provided." } }, "required": [ "name", "email" ], "additionalProperties": false, "description": "A normalized email address." }, "nullable": true, "handle": "from" }, { "jsonSchema": { "type": [ "string", "null" ], "description": "The email subject when available." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": [ "string", "null" ], "format": "date-time", "description": "The message date as an ISO 8601 timestamp." }, "nullable": true, "handle": "date" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "attachmentId": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "filename": { "type": [ "string", "null" ], "description": "The attachment filename when available." }, "contentType": { "type": [ "string", "null" ], "description": "The attachment MIME content type when available." }, "size": { "type": [ "integer", "null" ], "description": "The attachment size in bytes when available." }, "contentId": { "type": [ "string", "null" ], "description": "The attachment Content-ID when available." } }, "required": [ "attachmentId", "filename", "contentType", "size", "contentId" ], "additionalProperties": false, "description": "Attachment metadata without attachment content." }, "description": "Attachment metadata parsed from the message." }, "nullable": false, "handle": "attachments" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "match" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "nullable": false, "handle": "attachmentId" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "email" } ], "name": "Check the message body", "moduleId": "n_acb7b0426d85b589", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_01aabab60be94699": { "inputs": {}, "name": "Does it mention an invoice?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_8a134dc3b8043e80", "output": "match" } }, "operator": "isTrue" } ] } ] } ] }, "n_bbdb0706d011b3c9": { "inputs": { "folder": { "kind": "value", "value": "INBOX" }, "uid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "uid" } ] }, "attachmentId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8a134dc3b8043e80", "output": "attachmentId" } ] } }, "name": "Download the first attachment", "kind": "task", "task": { "inputs": [ { "description": "The IMAP folder path returned by list_folders.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path returned by list_folders.", "default": "INBOX" }, "nullable": true, "handle": "folder", "value": "INBOX" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "description": "The IMAP body part identifier returned in an attachment metadata item.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "nullable": false, "handle": "attachmentId" } ], "outputs": [ { "description": "The IMAP folder path that contained the message.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP folder path that contained the message." }, "nullable": false, "handle": "folder" }, { "description": "The IMAP UID of the message.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "description": "The IMAP body part identifier returned in an attachment metadata item.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "nullable": false, "handle": "attachmentId" }, { "description": "The attachment size in bytes when available.", "jsonSchema": { "type": [ "integer", "null" ], "description": "The attachment size in bytes when available." }, "nullable": true, "handle": "size" }, { "description": "A file uploaded to connector transit storage.", "jsonSchema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "description": "The file name used in transit storage." }, "mimetype": { "type": "string", "minLength": 1, "description": "The file MIME type." }, "s3url": { "type": "string", "format": "uri", "description": "The accessible transit URL for downloading the file." }, "size": { "type": [ "integer", "null" ], "description": "The file size in bytes when available." } }, "required": [ "name", "mimetype", "s3url", "size" ], "additionalProperties": false, "description": "A file uploaded to connector transit storage." }, "nullable": false, "handle": "file" } ], "name": "Download the first attachment", "executor": { "kind": "connector", "action": "generic_imap.download_attachment" } } }, "n_883d11a270116a91": { "inputs": { "uid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf2d961c61c50c9d", "output": "uid" } ] }, "attachmentId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8a134dc3b8043e80", "output": "attachmentId" } ] }, "actualUid": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bbdb0706d011b3c9", "output": "uid" } ] }, "actualId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bbdb0706d011b3c9", "output": "attachmentId" } ] }, "file": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bbdb0706d011b3c9", "output": "file" } ] } }, "name": "Prepare the attachment", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "uid" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "attachmentId" }, { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The IMAP UID of the message." }, "nullable": false, "handle": "actualUid" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The IMAP body part identifier returned in an attachment metadata item." }, "nullable": false, "handle": "actualId" }, { "jsonSchema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "description": "The file name used in transit storage." }, "mimetype": { "type": "string", "minLength": 1, "description": "The file MIME type." }, "s3url": { "type": "string", "format": "uri", "description": "The accessible transit URL for downloading the file." }, "size": { "type": [ "integer", "null" ], "description": "The file size in bytes when available." } }, "required": [ "name", "mimetype", "s3url", "size" ], "additionalProperties": false, "description": "A file uploaded to connector transit storage." }, "nullable": false, "handle": "file" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "minLength": 1, "description": "The filename to use for the outgoing attachment." }, "contentType": { "type": "string", "minLength": 1, "description": "The MIME content type of the attachment." }, "contentUrl": { "type": "string", "format": "uri", "description": "The public HTTP or HTTPS URL to fetch attachment content from." } }, "required": [ "filename", "contentUrl" ], "additionalProperties": false, "description": "An attachment to fetch and include in an outgoing email." }, "minItems": 1, "maxItems": 10, "description": "Attachments to fetch and include in the outgoing email." }, "nullable": true, "handle": "attachments" } ], "name": "Prepare the attachment", "moduleId": "n_27081d9ff69e5593", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_c79a32ce736ea020": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_883d11a270116a91", "output": "url" } ] }, "modelId": { "kind": "value", "value": "YOUR_MINDEE_MODEL_ID" }, "apiKey": { "kind": "unset" } }, "name": "Submit the invoice", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "modelId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "apiKey" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" } ], "name": "Submit the invoice", "moduleId": "n_251ee7375a3063a7", "capabilities": [ { "kind": "http", "hosts": [ "api-v2.mindee.net" ] } ] } }, "n_33e219a95c46edbc": { "inputs": {}, "name": "Wait for invoice processing", "kind": "wait", "inputDefinitions": [], "prompt": "Wait for invoice processing", "autoContinue": { "kind": "after", "durationMs": 3000 } }, "n_d3af801bae4f26bd": { "inputs": { "jobId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c79a32ce736ea020", "output": "jobId" } ] }, "apiKey": { "kind": "unset" }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d3af801bae4f26bd", "output": "state" } ] } }, "name": "Check invoice processing", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "apiKey" }, { "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": "ready" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "resultUrl" } ], "name": "Check invoice processing", "moduleId": "n_ad638a1db45883f9", "capabilities": [ { "kind": "http", "hosts": [ "api-v2.mindee.net" ] } ] } }, "n_5fc58ae4de59d0ef": { "inputs": {}, "name": "Is the invoice ready?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_d3af801bae4f26bd", "output": "ready" } }, "operator": "isTrue" } ] } ] } ] }, "n_116757223fffb9b0": { "inputs": { "resultUrl": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d3af801bae4f26bd", "output": "resultUrl" } ] }, "jobId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c79a32ce736ea020", "output": "jobId" } ] }, "apiKey": { "kind": "unset" } }, "name": "Read invoice results", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "resultUrl" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "apiKey" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "data" } ], "name": "Read invoice results", "moduleId": "n_b3d76873db6c18d2", "capabilities": [ { "kind": "http", "hosts": [ "api-v2.mindee.net" ] } ] } }, "n_88a7f759f4c6cf44": { "inputs": { "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_116757223fffb9b0", "output": "data" } ] }, "jobId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c79a32ce736ea020", "output": "jobId" } ] }, "email": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8a134dc3b8043e80", "output": "email" } ] } }, "name": "Prepare invoice notification", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobId" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "email" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "high" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": true, "handle": "attachments" } ], "name": "Prepare invoice notification", "moduleId": "n_600b111e3f870664", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_97a710374ed514b3": { "inputs": {}, "name": "Is the total over 1000?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_88a7f759f4c6cf44", "output": "high" } }, "operator": "isTrue" } ] } ] } ] }, "n_d033d842dd907888": { "inputs": { "to": { "kind": "value", "value": [ "finance@example.com" ] }, "subject": { "kind": "value", "value": "New high value invoice" }, "text": { "kind": "value", "value": "Hi,\n\nThere is a new high value invoice to be paid that you may need to approve.\n\n~ n8n workflow" }, "attachments": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_883d11a270116a91", "output": "attachments" } ] } }, "name": "Email finance manager", "kind": "task", "task": { "inputs": [ { "description": "The primary recipient email addresses.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "minItems": 1, "description": "The primary recipient email addresses." }, "nullable": false, "handle": "to" }, { "description": "The carbon-copy recipient email addresses.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "minItems": 1, "description": "The carbon-copy recipient email addresses." }, "nullable": true, "handle": "cc", "value": null }, { "description": "The blind-carbon-copy recipient email addresses.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "minItems": 1, "description": "The blind-carbon-copy recipient email addresses." }, "nullable": true, "handle": "bcc", "value": null }, { "description": "An email address.", "jsonSchema": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "nullable": true, "handle": "replyTo", "value": null }, { "description": "The email subject.", "jsonSchema": { "type": "string", "description": "The email subject." }, "nullable": false, "handle": "subject" }, { "description": "The plain text email body.", "jsonSchema": { "type": "string", "description": "The plain text email body." }, "nullable": true, "handle": "text", "value": null }, { "description": "The HTML email body.", "jsonSchema": { "type": "string", "description": "The HTML email body." }, "nullable": true, "handle": "html", "value": null }, { "description": "Attachments to fetch and include in the outgoing email.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "minLength": 1, "description": "The filename to use for the outgoing attachment." }, "contentType": { "type": "string", "minLength": 1, "description": "The MIME content type of the attachment." }, "contentUrl": { "type": "string", "format": "uri", "description": "The public HTTP or HTTPS URL to fetch attachment content from." } }, "required": [ "filename", "contentUrl" ], "additionalProperties": false, "description": "An attachment to fetch and include in an outgoing email." }, "minItems": 1, "maxItems": 10, "description": "Attachments to fetch and include in the outgoing email." }, "nullable": true, "handle": "attachments", "value": null } ], "outputs": [ { "description": "The SMTP Message-ID returned by the mail client.", "jsonSchema": { "type": [ "string", "null" ], "description": "The SMTP Message-ID returned by the mail client." }, "nullable": true, "handle": "messageId" }, { "description": "Recipient addresses accepted by the SMTP server.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "description": "Recipient addresses accepted by the SMTP server." }, "nullable": false, "handle": "accepted" }, { "description": "Recipient addresses rejected by the SMTP server.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "description": "Recipient addresses rejected by the SMTP server." }, "nullable": false, "handle": "rejected" }, { "description": "The SMTP response text returned by the server.", "jsonSchema": { "type": "string", "description": "The SMTP response text returned by the server." }, "nullable": false, "handle": "response" } ], "name": "Email finance manager", "executor": { "kind": "connector", "action": "generic_imap.send_email" } } }, "n_4dd979a34870a240": { "inputs": { "accepted": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d033d842dd907888", "output": "accepted" } ] }, "rejected": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d033d842dd907888", "output": "rejected" } ] } }, "name": "Confirm finance email", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "description": "Recipient addresses accepted by the SMTP server." }, "nullable": false, "handle": "accepted" }, { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "format": "email", "description": "An email address." }, "description": "Recipient addresses rejected by the SMTP server." }, "nullable": false, "handle": "rejected" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "confirmed" } ], "name": "Confirm finance email", "moduleId": "n_e050e6b984a8e89f", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_29355ec4fece70e5": { "inputs": { "channelId": { "kind": "value", "value": "YOUR_SLACK_CHANNEL_ID" }, "text": { "kind": "value", "value": ":new: There is a new invoice to pay :new:" }, "attachments": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_88a7f759f4c6cf44", "output": "attachments" } ] } }, "name": "Notify Slack", "kind": "task", "task": { "inputs": [ { "description": "The Slack conversation or channel ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Slack conversation or channel ID." }, "nullable": false, "handle": "channelId" }, { "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text.", "jsonSchema": { "type": "string", "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text." }, "nullable": true, "handle": "text", "value": null }, { "description": "Slack Block Kit blocks to render in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack Block Kit block object. Pass the block exactly as Slack documents it." }, "minItems": 1, "description": "Slack Block Kit blocks to render in the message." }, "nullable": true, "handle": "blocks", "value": null }, { "description": "Slack legacy attachments to include in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Whether Slack should unfurl links in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl links in the message." }, "nullable": true, "handle": "unfurlLinks", "value": null }, { "description": "Whether Slack should unfurl media in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl media in the message." }, "nullable": true, "handle": "unfurlMedia", "value": null }, { "description": "Slack message metadata to attach to the message.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack message metadata to attach to the message." }, "nullable": true, "handle": "metadata", "value": null } ], "outputs": [ { "description": "The timestamp identifier of the posted message.", "jsonSchema": { "type": "string", "description": "The timestamp identifier of the posted message." }, "nullable": false, "handle": "ts" }, { "description": "The channel ID where the message was posted.", "jsonSchema": { "type": "string", "description": "The channel ID where the message was posted." }, "nullable": false, "handle": "channelId" } ], "name": "Notify Slack", "executor": { "kind": "connector", "action": "slack.post_message" } } } }, "edges": [ { "source": "n_f5451b5322674882", "target": "n_9b680e4e27d17c86" }, { "source": "n_9b680e4e27d17c86", "target": "n_bf2d961c61c50c9d" }, { "source": "n_bf2d961c61c50c9d", "target": "n_c4578d179ae78905" }, { "source": "n_0dea1ee79591c4e8", "target": "n_fa5754b668f7d931" }, { "source": "n_fa5754b668f7d931", "target": "n_8a134dc3b8043e80" }, { "source": "n_8a134dc3b8043e80", "target": "n_01aabab60be94699" }, { "source": "n_bbdb0706d011b3c9", "target": "n_883d11a270116a91" }, { "source": "n_883d11a270116a91", "target": "n_c79a32ce736ea020" }, { "source": "n_c79a32ce736ea020", "target": "n_33e219a95c46edbc" }, { "source": "n_d3af801bae4f26bd", "target": "n_5fc58ae4de59d0ef" }, { "source": "n_116757223fffb9b0", "target": "n_88a7f759f4c6cf44" }, { "source": "n_88a7f759f4c6cf44", "target": "n_97a710374ed514b3" }, { "source": "n_d033d842dd907888", "target": "n_4dd979a34870a240" }, { "source": "n_4dd979a34870a240", "target": "n_29355ec4fece70e5" }, { "source": "n_29355ec4fece70e5", "target": "n_9b680e4e27d17c86" }, { "source": "n_c4578d179ae78905", "target": "n_0dea1ee79591c4e8", "sourceHandle": "yes" }, { "source": "n_01aabab60be94699", "target": "n_bbdb0706d011b3c9", "sourceHandle": "yes" }, { "source": "n_01aabab60be94699", "target": "n_9b680e4e27d17c86", "sourceHandle": "otherwise" }, { "source": "n_33e219a95c46edbc", "target": "n_d3af801bae4f26bd", "sourceHandle": "continue" }, { "source": "n_5fc58ae4de59d0ef", "target": "n_116757223fffb9b0", "sourceHandle": "yes" }, { "source": "n_5fc58ae4de59d0ef", "target": "n_33e219a95c46edbc", "sourceHandle": "otherwise" }, { "source": "n_97a710374ed514b3", "target": "n_d033d842dd907888", "sourceHandle": "yes" }, { "source": "n_97a710374ed514b3", "target": "n_29355ec4fece70e5", "sourceHandle": "otherwise" } ] } }, "modules": { "n_0b8253bde36637a2": { "name": "Select the message", "imports": [], "source": "export default function pickInvoiceMail({emails}) {\n if (!Array.isArray(emails)) throw Error('Invalid IMAP search response');\n if (emails.length === 0) return {more:false,uid:1};\n const uid=emails[0]?.uid;\n if (!Number.isSafeInteger(uid)||uid<1) throw Error('Invalid IMAP UID');\n return {more:true,uid};\n}\n" }, "n_acb7b0426d85b589": { "name": "Check the message body", "imports": [], "source": "export default function inspectInvoiceMail({uid,actualUid,text,truncated,from,subject,date,attachments}) {\n if (actualUid!==uid || truncated) throw Error('Cannot inspect the complete email');\n if (typeof text!=='string') throw Error('Email has no plain text body');\n const match=text.toLowerCase().includes('invoice');\n if (!match) return {match:false,attachmentId:'unused',email:{}};\n const first=attachments?.[0];\n if (!first || typeof first.attachmentId!=='string'||!first.attachmentId || !from?.email || typeof subject!=='string' || typeof date!=='string') throw Error('Invoice email requires its first attachment, sender, subject and date');\n return {match:true,attachmentId:first.attachmentId,email:{from:from.email,subject,date}};\n}\n" }, "n_27081d9ff69e5593": { "name": "Prepare the attachment", "imports": [], "source": "export default function prepareInvoiceAttachment({uid,attachmentId,actualUid,actualId,file}) {\n if(uid!==actualUid||attachmentId!==actualId||!file||typeof file.name!=='string'||!file.name||typeof file.mimetype!=='string'||!file.mimetype||!/^https:\\/\\/[^\\s/]+/.test(file.s3url||'')) throw Error('Invalid downloaded attachment');\n return {url:file.s3url,attachments:[{filename:file.name,contentType:file.mimetype,contentUrl:file.s3url}]};\n}\n" }, "n_251ee7375a3063a7": { "name": "Submit the invoice", "imports": [], "source": "export default async function enqueueReceipt({url,modelId,apiKey},context) {\n if(!/^https:\\/\\/[^\\s/?#]+(?:[/?#][^\\s]*)?$/i.test(url)||! /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(modelId)||typeof apiKey!=='string'||!apiKey.trim())throw Error('Configure receipt URL, Mindee model ID and API key');\n const boundary='openflow-mindee-form-7d1b8e6f';if(url.includes(boundary))throw Error('Invalid URL');\n const body=`--${boundary}\\r\\nContent-Disposition: form-data; name=\"model_id\"\\r\\n\\r\\n${modelId}\\r\\n--${boundary}\\r\\nContent-Disposition: form-data; name=\"url\"\\r\\n\\r\\n${url}\\r\\n--${boundary}--\\r\\n`;\n const r=await context.fetch('https://api-v2.mindee.net/v2/products/extraction/enqueue',{method:'POST',headers:{Authorization:apiKey,'Content-Type':'multipart/form-data; boundary='+boundary},body});if(r.status!==202)throw Error('Mindee enqueue failed or outcome unknown');const data=await r.json();if(!/^[0-9a-f-]{36}$/i.test(data?.job?.id??'')||data.job.model_id!==modelId)throw Error('Mindee returned an invalid job');return{jobId:data.job.id};\n}\n" }, "n_ad638a1db45883f9": { "name": "Check invoice processing", "imports": [], "source": "export default async function pollReceipt({jobId,apiKey,previousState},context) {\n const attempt=(previousState?.jobId===jobId?previousState.attempt:0)+1;if(attempt>200)throw Error('Mindee did not finish within the polling window');\n if(!/^[0-9a-f-]{36}$/i.test(jobId)||typeof apiKey!=='string'||!apiKey.trim())throw Error('Invalid Mindee job or key');\n const r=await context.fetch(`https://api-v2.mindee.net/v2/jobs/${jobId}?redirect=false`,{headers:{Authorization:apiKey}});if(!r.ok)throw Error('Mindee job lookup failed');const data=await r.json(),job=data?.job;if(job?.id!==jobId)throw Error('Mindee job mismatch');\n if(job.status==='Failed')throw Error('Mindee receipt processing failed');if(job.status==='Processing')return{state:{jobId,attempt},ready:false,resultUrl:'https://api-v2.mindee.net/v2/products/extraction/results/unused'};\n if(job.status!=='Processed'||!/^https:\\/\\/api-v2\\.mindee\\.net\\/v2\\/products\\/extraction\\/results\\/[0-9a-f-]{36}$/i.test(job.result_url??''))throw Error('Invalid Mindee result URL');return{state:{jobId,attempt},ready:true,resultUrl:job.result_url};\n}\n" }, "n_b3d76873db6c18d2": { "name": "Read invoice results", "imports": [], "source": "export default async function readReceipt({resultUrl,jobId,apiKey},context){\n if(!/^https:\\/\\/api-v2\\.mindee\\.net\\/v2\\/products\\/extraction\\/results\\/[0-9a-f-]{36}$/i.test(resultUrl))throw Error('Invalid Mindee result URL');\n const r=await context.fetch(resultUrl,{headers:{Authorization:apiKey}});if(!r.ok)throw Error('Mindee result request failed');return{data:await r.json()};\n}\n" }, "n_600b111e3f870664": { "name": "Prepare invoice notification", "imports": [], "source": "export default function invoiceAlert({data,jobId,email}) {\n if(data?.inference?.job?.id!==jobId)throw Error('Mindee result job mismatch');\n const amount=data?.inference?.result?.fields?.total_amount?.value;\n if(typeof amount!=='number'||!Number.isFinite(amount))throw Error('Invoice total was not recognised');\n if(!email||typeof email.from!=='string'||typeof email.subject!=='string'||typeof email.date!=='string')throw Error('Invalid email details');\n return {high:amount>1000,attachments:[{color:'#FFBF00',fields:[{short:true,title:'Amount',value:String(amount)},{short:false,title:'From',value:email.from},{short:true,title:'Subject',value:email.subject}],footer:'*Date:* '+email.date}]};\n}\n" }, "n_e050e6b984a8e89f": { "name": "Confirm finance email", "imports": [], "source": "export default function invoiceSmtpConfirm({accepted,rejected}) {\n if(!Array.isArray(accepted)||!accepted.length||!Array.isArray(rejected)||rejected.length)throw Error('SMTP did not accept the finance email');\n return {confirmed:true};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }