{ "kind": "open-flow-workflow", "version": 1, "name": "Send New Upwork Jobs to Slack", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_8a58f80e0118f1d1": { "name": "Every ten minutes", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "*/10 * * * *", "timezone": "UTC" } ] }, "n_b957e07d93af21c6": { "inputs": { "scheduledAt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8a58f80e0118f1d1", "output": "scheduledAt" } ] } }, "name": "Check working hours", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "scheduledAt" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "open" } ], "name": "Check working hours", "moduleId": "n_f2c07656f69e5b2c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_65beb14d79c1c661": { "inputs": {}, "name": "During working hours?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_b957e07d93af21c6", "output": "open" } }, "operator": "isTrue" } ] } ] } ] }, "n_7a8b8e1e2600ff1e": { "inputs": { "actorId": { "kind": "value", "value": "arlusm~upwork-scraper-with-fresh-job-posts" }, "input": { "kind": "value", "value": { "startUrls": [ { "url": "https://www.upwork.com/nx/search/jobs/?nbs=1&q=python", "method": "GET" }, { "url": "https://www.upwork.com/nx/search/jobs/?nbs=1&q=java", "method": "GET" } ], "proxyCountryCode": "FR" } } }, "name": "Start the Upwork search", "kind": "task", "task": { "inputs": [ { "description": "The Apify actor identifier, such as apify~web-scraper or apify/web-scraper.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Apify actor identifier, such as apify~web-scraper or apify/web-scraper." }, "nullable": false, "handle": "actorId" }, { "description": "The JSON input object passed to the actor run.", "jsonSchema": { "type": "object", "additionalProperties": { "description": "A JSON-compatible Apify value." }, "description": "The JSON input object passed to the actor run." }, "nullable": true, "handle": "input", "value": null }, { "description": "The actor build tag or number to run.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The actor build tag or number to run." }, "nullable": true, "handle": "build", "value": null }, { "description": "The memory limit for the run in megabytes.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The memory limit for the run in megabytes." }, "nullable": true, "handle": "memoryMbytes", "value": null }, { "description": "The maximum runtime for the run in seconds.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The maximum runtime for the run in seconds." }, "nullable": true, "handle": "timeoutSecs", "value": null } ], "outputs": [ { "description": "An Apify actor run.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "string", "description": "The Apify run identifier." }, "actId": { "type": "string", "description": "The actor identifier associated with the run." }, "status": { "type": "string", "description": "The current run status." }, "startedAt": { "type": "string", "description": "When the run started." }, "finishedAt": { "type": "string", "description": "When the run finished." }, "defaultDatasetId": { "type": "string", "description": "The default dataset identifier created for the run." }, "defaultKeyValueStoreId": { "type": "string", "description": "The default key-value store identifier created for the run." }, "defaultRequestQueueId": { "type": "string", "description": "The default request queue identifier created for the run." }, "stats": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run statistics object." }, "options": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run options object." }, "usage": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run usage summary object." } }, "required": [ "id", "actId", "status" ], "additionalProperties": true, "description": "An Apify actor run." }, "nullable": false, "handle": "run" } ], "name": "Start the Upwork search", "executor": { "kind": "connector", "action": "apify.run_actor" } } }, "n_5861d9f46b9425e6": { "inputs": { "initial": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7a8b8e1e2600ff1e", "output": "run" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5861d9f46b9425e6", "output": "state" } ] }, "current": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b27b429dee293953", "output": "run" } ] } }, "name": "Check the search run", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": { "id": { "type": "string", "description": "The Apify run identifier." }, "actId": { "type": "string", "description": "The actor identifier associated with the run." }, "status": { "type": "string", "description": "The current run status." }, "startedAt": { "type": "string", "description": "When the run started." }, "finishedAt": { "type": "string", "description": "When the run finished." }, "defaultDatasetId": { "type": "string", "description": "The default dataset identifier created for the run." }, "defaultKeyValueStoreId": { "type": "string", "description": "The default key-value store identifier created for the run." }, "defaultRequestQueueId": { "type": "string", "description": "The default request queue identifier created for the run." }, "stats": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run statistics object." }, "options": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run options object." }, "usage": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run usage summary object." } }, "required": [ "id", "actId", "status" ], "additionalProperties": true, "description": "An Apify actor run." }, "nullable": false, "handle": "initial" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "current" }, { "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": "done" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The Apify actor run identifier." }, "nullable": false, "handle": "runId" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The Apify dataset identifier." }, "nullable": false, "handle": "datasetId" } ], "name": "Check the search run", "moduleId": "n_668d8124b62e6d0d", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_47c19fa1c89b6085": { "inputs": {}, "name": "Search complete?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_5861d9f46b9425e6", "output": "done" } }, "operator": "isTrue" } ] } ] } ] }, "n_3a01211a1e64a054": { "inputs": {}, "name": "Wait for the search", "kind": "wait", "inputDefinitions": [], "prompt": "Wait for the search", "autoContinue": { "kind": "after", "durationMs": 15000 } }, "n_b27b429dee293953": { "inputs": { "runId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5861d9f46b9425e6", "output": "runId" } ] }, "waitForFinishSeconds": { "kind": "value", "value": 0 } }, "name": "Read search status", "kind": "task", "task": { "inputs": [ { "description": "The Apify actor run identifier.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Apify actor run identifier." }, "nullable": false, "handle": "runId" }, { "description": "How many seconds to wait for run completion before returning.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "How many seconds to wait for run completion before returning." }, "nullable": true, "handle": "waitForFinishSeconds", "value": null } ], "outputs": [ { "description": "An Apify actor run.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "string", "description": "The Apify run identifier." }, "actId": { "type": "string", "description": "The actor identifier associated with the run." }, "status": { "type": "string", "description": "The current run status." }, "startedAt": { "type": "string", "description": "When the run started." }, "finishedAt": { "type": "string", "description": "When the run finished." }, "defaultDatasetId": { "type": "string", "description": "The default dataset identifier created for the run." }, "defaultKeyValueStoreId": { "type": "string", "description": "The default key-value store identifier created for the run." }, "defaultRequestQueueId": { "type": "string", "description": "The default request queue identifier created for the run." }, "stats": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run statistics object." }, "options": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run options object." }, "usage": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The run usage summary object." } }, "required": [ "id", "actId", "status" ], "additionalProperties": true, "description": "An Apify actor run." }, "nullable": false, "handle": "run" } ], "name": "Read search status", "executor": { "kind": "connector", "action": "apify.get_run" } } }, "n_b765936f0bcdee4e": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b765936f0bcdee4e", "output": "state" } ] }, "previousItems": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c9311c95bcc814a6", "output": "items" } ] } }, "name": "Collect search results", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": true, "handle": "previousItems" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "integer", "minimum": 0, "description": "How many items to skip before returning results." }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "jobs" } ], "name": "Collect search results", "moduleId": "n_ff27d3fe9abc5bba", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_eb7463b4e886559d": { "inputs": {}, "name": "More results to read?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_b765936f0bcdee4e", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_c9311c95bcc814a6": { "inputs": { "datasetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5861d9f46b9425e6", "output": "datasetId" } ] }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b765936f0bcdee4e", "output": "offset" } ] }, "limit": { "kind": "value", "value": 100 } }, "name": "Read a result page", "kind": "task", "task": { "inputs": [ { "description": "The Apify dataset identifier.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Apify dataset identifier." }, "nullable": false, "handle": "datasetId" }, { "description": "The maximum number of items to return.", "jsonSchema": { "type": "integer", "minimum": 1, "description": "The maximum number of items to return." }, "nullable": true, "handle": "limit", "value": null }, { "description": "How many items to skip before returning results.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "How many items to skip before returning results." }, "nullable": true, "handle": "offset", "value": null }, { "description": "Whether hidden fields and empty values should be removed from each item.", "jsonSchema": { "type": "boolean", "description": "Whether hidden fields and empty values should be removed from each item." }, "nullable": true, "handle": "clean", "value": null }, { "description": "Whether fields starting with a hash sign should be skipped.", "jsonSchema": { "type": "boolean", "description": "Whether fields starting with a hash sign should be skipped." }, "nullable": true, "handle": "skipHidden", "value": null } ], "outputs": [ { "description": "The ordered list of Apify dataset items.", "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": { "description": "A JSON-compatible Apify value." }, "description": "A single item returned from an Apify dataset." }, "description": "The ordered list of Apify dataset items." }, "nullable": false, "handle": "items" } ], "name": "Read a result page", "executor": { "kind": "connector", "action": "apify.get_dataset_items" } } }, "n_c12a43bd1d4d031a": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c12a43bd1d4d031a", "output": "state" } ] }, "previousRecords": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_84c6cf4d52f6a981", "output": "records" } ] }, "previousOffset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_84c6cf4d52f6a981", "output": "offset" } ] } }, "name": "Collect saved job records", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": true, "handle": "previousRecords" }, { "jsonSchema": { "type": [ "string", "null" ] }, "nullable": true, "handle": "previousOffset" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "Opaque pagination cursor returned by a previous Airtable response." }, "nullable": true, "handle": "offset" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "records" } ], "name": "Collect saved job records", "moduleId": "n_5d82b2a8ace7d782", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_5d8bbc27b8a5a11e": { "inputs": {}, "name": "More saved records?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_c12a43bd1d4d031a", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_84c6cf4d52f6a981": { "inputs": { "baseId": { "kind": "value", "value": "YOUR_AIRTABLE_BASE_ID" }, "tableIdOrName": { "kind": "value", "value": "Jobs" }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c12a43bd1d4d031a", "output": "offset" } ] }, "pageSize": { "kind": "value", "value": 100 } }, "name": "Read saved job records", "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": "Read saved job records", "executor": { "kind": "connector", "action": "airtable.list_records" } } }, "n_4c83b8493f19f6d9": { "inputs": { "jobs": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b765936f0bcdee4e", "output": "jobs" } ] }, "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c12a43bd1d4d031a", "output": "records" } ] } }, "name": "Find new jobs", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "jobs" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "records" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "messages" } ], "name": "Find new jobs", "moduleId": "n_41f40c14bd4312b0", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_500edfedc6fe002a": { "inputs": { "messages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4c83b8493f19f6d9", "output": "messages" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_500edfedc6fe002a", "output": "state" } ] } }, "name": "Select the next job", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "messages" }, { "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": "string", "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text." }, "nullable": true, "handle": "text" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values for the new Airtable record." } }, "required": [ "fields" ], "additionalProperties": false, "description": "Record payload used when creating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to create in the Airtable table." }, "nullable": false, "handle": "records" } ], "name": "Select the next job", "moduleId": "n_113250cf2b32800a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_e04e2bd089f963c9": { "inputs": {}, "name": "More new jobs?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_500edfedc6fe002a", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_e1c22cc8648cf86d": { "inputs": { "baseId": { "kind": "value", "value": "YOUR_AIRTABLE_BASE_ID" }, "tableIdOrName": { "kind": "value", "value": "Jobs" }, "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_500edfedc6fe002a", "output": "records" } ] } }, "name": "Save the job 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 create in the Airtable table.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values for the new Airtable record." } }, "required": [ "fields" ], "additionalProperties": false, "description": "Record payload used when creating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to create 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 created 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 created by Airtable." }, "nullable": false, "handle": "records" } ], "name": "Save the job record", "executor": { "kind": "connector", "action": "airtable.create_records" } } }, "n_8e7aeeff60a52386": { "inputs": { "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e1c22cc8648cf86d", "output": "records" } ] } }, "name": "Confirm the saved job", "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 created by Airtable." }, "nullable": false, "handle": "records" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "ok" } ], "name": "Confirm the saved job", "moduleId": "n_f410aa982572c91d", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a2f0c940d9c8d3dd": { "inputs": { "channelId": { "kind": "value", "value": "YOUR_SLACK_CHANNEL_ID" }, "text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_500edfedc6fe002a", "output": "text" } ] } }, "name": "Post the job to 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": "Post the job to Slack", "executor": { "kind": "connector", "action": "slack.post_message" } } } }, "edges": [ { "source": "n_8a58f80e0118f1d1", "target": "n_b957e07d93af21c6" }, { "source": "n_b957e07d93af21c6", "target": "n_65beb14d79c1c661" }, { "source": "n_65beb14d79c1c661", "target": "n_7a8b8e1e2600ff1e", "sourceHandle": "yes" }, { "source": "n_7a8b8e1e2600ff1e", "target": "n_5861d9f46b9425e6" }, { "source": "n_5861d9f46b9425e6", "target": "n_47c19fa1c89b6085" }, { "source": "n_47c19fa1c89b6085", "target": "n_b765936f0bcdee4e", "sourceHandle": "yes" }, { "source": "n_47c19fa1c89b6085", "target": "n_3a01211a1e64a054", "sourceHandle": "otherwise" }, { "source": "n_3a01211a1e64a054", "target": "n_b27b429dee293953", "sourceHandle": "continue" }, { "source": "n_b27b429dee293953", "target": "n_5861d9f46b9425e6" }, { "source": "n_b765936f0bcdee4e", "target": "n_eb7463b4e886559d" }, { "source": "n_eb7463b4e886559d", "target": "n_c9311c95bcc814a6", "sourceHandle": "yes" }, { "source": "n_c9311c95bcc814a6", "target": "n_b765936f0bcdee4e" }, { "source": "n_eb7463b4e886559d", "target": "n_c12a43bd1d4d031a", "sourceHandle": "otherwise" }, { "source": "n_c12a43bd1d4d031a", "target": "n_5d8bbc27b8a5a11e" }, { "source": "n_5d8bbc27b8a5a11e", "target": "n_84c6cf4d52f6a981", "sourceHandle": "yes" }, { "source": "n_84c6cf4d52f6a981", "target": "n_c12a43bd1d4d031a" }, { "source": "n_5d8bbc27b8a5a11e", "target": "n_4c83b8493f19f6d9", "sourceHandle": "otherwise" }, { "source": "n_4c83b8493f19f6d9", "target": "n_500edfedc6fe002a" }, { "source": "n_500edfedc6fe002a", "target": "n_e04e2bd089f963c9" }, { "source": "n_e04e2bd089f963c9", "target": "n_e1c22cc8648cf86d", "sourceHandle": "yes" }, { "source": "n_e1c22cc8648cf86d", "target": "n_8e7aeeff60a52386" }, { "source": "n_8e7aeeff60a52386", "target": "n_a2f0c940d9c8d3dd" }, { "source": "n_a2f0c940d9c8d3dd", "target": "n_500edfedc6fe002a" } ] } }, "modules": { "n_f2c07656f69e5b2c": { "name": "Check working hours", "imports": [], "source": "export default function hours({scheduledAt}){const date=new Date(scheduledAt);if(!Number.isFinite(date.getTime()))throw Error('Invalid schedule time');const hour=date.getUTCHours();return{open:hour>2&&hour<15}}\n" }, "n_668d8124b62e6d0d": { "name": "Check the search run", "imports": [], "source": "export default function run({initial,current,previousState}){const data=current??initial;if(!data||typeof data.id!=='string'||!data.id)throw Error('Missing Apify run ID');const count=previousState?.count??0;if(!Number.isInteger(count)||count<0||count>20)throw Error('Invalid poll count');if(data.status==='SUCCEEDED'){if(typeof data.defaultDatasetId!=='string'||!data.defaultDatasetId)throw Error('Missing dataset ID');return{state:{count:count+1},done:true,runId:data.id,datasetId:data.defaultDatasetId}}if(!['READY','RUNNING'].includes(data.status))throw Error('Apify run failed or stopped');if(count>=20)throw Error('Apify run did not finish in time');return{state:{count:count+1},done:false,runId:data.id,datasetId:'pending'}}\n" }, "n_ff27d3fe9abc5bba": { "name": "Collect search results", "imports": [], "source": "export default function pages({previousState,previousItems}){const state=previousState??{offset:0,items:[],started:false};if(!Number.isInteger(state.offset)||!Array.isArray(state.items)||typeof state.started!=='boolean')throw Error('Invalid dataset cursor');if(!state.started)return{state:{offset:0,items:[],started:true},more:true,offset:0,jobs:[]};if(!Array.isArray(previousItems))throw Error('Invalid dataset page');const items=[...state.items,...previousItems];const more=previousItems.length===100;return{state:{offset:state.offset+previousItems.length,items,started:true},more,offset:state.offset+previousItems.length,jobs:items}}\n" }, "n_5d82b2a8ace7d782": { "name": "Collect saved job records", "imports": [], "source": "export default function pages({previousState,previousRecords,previousOffset}){const s=previousState??{started:false,records:[],seen:[]};if(!Array.isArray(s.records)||!Array.isArray(s.seen))throw Error('Invalid record cursor');if(!s.started)return{state:{started:true,records:[],seen:[]},more:true,offset:null,records:[]};if(!Array.isArray(previousRecords))throw Error('Invalid record page');const records=[...s.records,...previousRecords];if(previousOffset==null)return{state:{started:true,records,seen:s.seen},more:false,offset:null,records};if(typeof previousOffset!=='string'||!previousOffset||s.seen.includes(previousOffset))throw Error('Invalid or repeated record cursor');return{state:{started:true,records,seen:[...s.seen,previousOffset]},more:true,offset:previousOffset,records}}\n" }, "n_41f40c14bd4312b0": { "name": "Find new jobs", "imports": [], "source": "export default function plan({jobs,records}){if(!Array.isArray(jobs)||!Array.isArray(records))throw Error('Invalid dataset');const seen=new Set(records.map(r=>{if(!r||!r.fields||typeof r.fields.title!=='string')throw Error('Invalid saved record');return JSON.stringify([r.fields.title,r.fields.budget??''])})),messages=[];const show=v=>Array.isArray(v)?v.join(', '):String(v??'');for(const x of jobs){if(!x||typeof x.title!=='string'||!x.title||typeof x.link!=='string'||!x.link)throw Error('Missing job title or link');const key=JSON.stringify([x.title,show(x.budget)]);if(seen.has(key))continue;seen.add(key);const text=['Job Title : '+x.title,'Published : '+show(x.publishedDate),'Link : '+x.link,'Payment Type: '+show(x.paymentType),'Budget: '+show(x.budget),'Skills: '+show(x.skills),'Bio: '+show(x.shortBio)].join('\\n'),fields={};for(const k of ['title','link','paymentType','budget','projectLength','shortBio','skills','publishedDate','normalizedDate','searchUrl'])fields[k]=show(x[k]);fields['Raw job JSON']=JSON.stringify(x);messages.push({text,fields})}return{messages}}\n" }, "n_113250cf2b32800a": { "name": "Select the next job", "imports": [], "source": "export default function next({messages,previousState}){if(!Array.isArray(messages))throw Error('Invalid messages');const index=previousState?.index??0;if(!Number.isInteger(index)||index<0||index>messages.length)throw Error('Invalid message index');const m=messages[index];return{state:{index:index+1},more:index