AutomationFlowsSlack & Telegram › Smb Demo - Confirmed Time Entry to Slack

Smb Demo - Confirmed Time Entry to Slack

SMB Demo - Confirmed Time Entry to Slack. Uses httpRequest. Webhook trigger; 10 nodes.

Webhook trigger★★★★☆ complexity10 nodesHTTP Request
Slack & Telegram Trigger: Webhook Nodes: 10 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "SMB Demo - Confirmed Time Entry to Slack",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "copilot-confirmed-v1",
        "responseMode": "onReceived",
        "options": {
          "rawBody": true
        }
      },
      "id": "708f09a1-f92e-4e30-8171-0affd43da87f",
      "name": "Confirmed Event Webhook Raw Body",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -960,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst secret = $env.COPILOT_OUTBOUND_HMAC_SECRET;\nif (!secret) throw new Error('COPILOT_OUTBOUND_HMAC_SECRET is required');\nconst headers = Object.fromEntries(Object.entries($json.headers || {}).map(([k,v]) => [k.toLowerCase(), String(v)]));\nconst timestamp = headers['x-acme-timestamp'];\nconst nonce = headers['x-acme-nonce'];\nconst supplied = headers['x-acme-signature'] || '';\nconst raw = typeof $json.rawBody === 'string' ? $json.rawBody : (typeof $json.body === 'string' ? $json.body : '');\nif (!raw || !timestamp || !nonce || !/^v1=[a-f0-9]{64}$/.test(supplied)) throw new Error('invalid_signature');\nif (Math.abs(Math.floor(Date.now()/1000) - Number(timestamp)) > 300) throw new Error('expired_signature');\nconst path = $env.COPILOT_OUTBOUND_WEBHOOK_PATH;\nif (!path) throw new Error('COPILOT_OUTBOUND_WEBHOOK_PATH is required');\nconst digest = crypto.createHash('sha256').update(raw).digest('hex');\nconst base = ['v1',timestamp,nonce,'POST',path,digest].join('\\n');\nconst expected = 'v1=' + crypto.createHmac('sha256',secret).update(base).digest('hex');\nif (supplied.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(supplied),Buffer.from(expected))) throw new Error('invalid_signature');\nconst event = JSON.parse(raw);\nconst keys = Object.keys(event).sort().join(',');\nconst resultKeys = Object.keys(event.result || {}).sort().join(',');\nif (keys !== 'event_id,event_type,occurred_at,request_id,result,schema_version' || resultKeys !== 'hours,person_display_name,project_display_name,status,time_entry_id,work_date' || event.schema_version !== '1.0' || event.event_type !== 'time_entry.confirmed' || !/^[0-9a-f-]{36}$/i.test(event.event_id)) throw new Error('invalid_confirmed_event');\nreturn [{json:{event}}];"
      },
      "id": "d710f66d-e250-46ba-866e-40820e7c1601",
      "name": "Verify HMAC Then Validate Confirmed Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -720,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst event = $json.event; const path = '/api/v1/integrations/notifications/' + event.event_id + ':claim'; const body = JSON.stringify({channel_ref:$env.SLACK_CHANNEL_REF}); const timestamp=Math.floor(Date.now()/1000).toString(); const nonce=crypto.randomUUID(); const digest=crypto.createHash('sha256').update(body).digest('hex'); const base=['v1',timestamp,nonce,'POST',path,digest].join('\\n'); const secret=$env.COPILOT_NOTIFICATION_CALLBACK_HMAC_SECRET; if(!secret || !$env.SLACK_CHANNEL_REF) throw new Error('callback secret and channel ref are required'); const signature='v1='+crypto.createHmac('sha256',secret).update(base).digest('hex'); return [{json:{event,path,body,timestamp,nonce,signature}}];"
      },
      "id": "33b0c767-96f0-46ee-a4d5-e28938354937",
      "name": "Sign Delivery Claim",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -480,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.COPILOT_CORE_BASE_URL + $json.path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Acme-Integration-Id",
              "value": "n8n-notification-v1"
            },
            {
              "name": "X-Acme-Timestamp",
              "value": "={{ $json.timestamp }}"
            },
            {
              "name": "X-Acme-Nonce",
              "value": "={{ $json.nonce }}"
            },
            {
              "name": "X-Acme-Signature",
              "value": "={{ $json.signature }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.body }}",
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "7c88c8d0-a08e-49c1-a38d-a27e26430827",
      "name": "Claim Before Slack",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -240,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "a18b971b-6f19-4b83-bc51-11e1e3e3b30c",
              "leftValue": "={{ $json.body.claim_granted }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "bdc91c91-d33c-4585-9298-bce35282f9f7",
      "name": "Claim Granted?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const event=$('Verify HMAC Then Validate Confirmed Event').first().json.event; const attempt=$json.body.delivery_attempt_id; const r=event.result; const text=['Time entry confirmed',r.person_display_name+' \u00b7 '+r.project_display_name,r.work_date+' \u00b7 '+r.hours+' hours \u00b7 '+r.status,'Reference: time entry #'+r.time_entry_id].join('\\n'); return [{json:{event,delivery_attempt_id:attempt,slack_body:JSON.stringify({text})}}];"
      },
      "id": "774851b0-d127-4c5f-800a-4c7fa33bf644",
      "name": "Build Fixed Slack Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        -96
      ]
    },
    {
      "parameters": {
        "amount": 1,
        "unit": "seconds"
      },
      "id": "ed39034b-afd6-493a-a45c-3bd398633068",
      "name": "One Message Per Second Gate",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        480,
        -96
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.SLACK_WEBHOOK_URL }}",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.slack_body }}",
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "db415c66-993f-46c4-9586-34a2ce046ec8",
      "name": "Slack Incoming Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        720,
        -96
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto=require('crypto'); const prior=$('Build Fixed Slack Payload').first().json; const statusCode=Number($json.statusCode || 0); const status=statusCode >= 200 && statusCode < 300 ? 'delivered' : (statusCode ? 'failed' : 'delivery_unknown'); const path='/api/v1/integrations/notifications/'+prior.event.event_id+':complete'; const body=JSON.stringify({delivery_attempt_id:prior.delivery_attempt_id,status}); const timestamp=Math.floor(Date.now()/1000).toString(); const nonce=crypto.randomUUID(); const digest=crypto.createHash('sha256').update(body).digest('hex'); const base=['v1',timestamp,nonce,'POST',path,digest].join('\\n'); const secret=$env.COPILOT_NOTIFICATION_CALLBACK_HMAC_SECRET; const signature='v1='+crypto.createHmac('sha256',secret).update(base).digest('hex'); return [{json:{path,body,timestamp,nonce,signature}}];"
      },
      "id": "3268f0e6-2ff0-4e71-a5fa-a35f6b729339",
      "name": "Classify Result and Sign Completion",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        -96
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.COPILOT_CORE_BASE_URL + $json.path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Acme-Integration-Id",
              "value": "n8n-notification-v1"
            },
            {
              "name": "X-Acme-Timestamp",
              "value": "={{ $json.timestamp }}"
            },
            {
              "name": "X-Acme-Nonce",
              "value": "={{ $json.nonce }}"
            },
            {
              "name": "X-Acme-Signature",
              "value": "={{ $json.signature }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.body }}",
        "options": {
          "timeout": 10000
        }
      },
      "id": "859681f9-0364-4639-aeb8-decc6acef65d",
      "name": "Complete Delivery Ledger",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1200,
        -96
      ]
    }
  ],
  "connections": {
    "Confirmed Event Webhook Raw Body": {
      "main": [
        [
          {
            "node": "Verify HMAC Then Validate Confirmed Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify HMAC Then Validate Confirmed Event": {
      "main": [
        [
          {
            "node": "Sign Delivery Claim",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sign Delivery Claim": {
      "main": [
        [
          {
            "node": "Claim Before Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claim Before Slack": {
      "main": [
        [
          {
            "node": "Claim Granted?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claim Granted?": {
      "main": [
        [
          {
            "node": "Build Fixed Slack Payload",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Build Fixed Slack Payload": {
      "main": [
        [
          {
            "node": "One Message Per Second Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "One Message Per Second Gate": {
      "main": [
        [
          {
            "node": "Slack Incoming Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Incoming Webhook": {
      "main": [
        [
          {
            "node": "Classify Result and Sign Completion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Result and Sign Completion": {
      "main": [
        [
          {
            "node": "Complete Delivery Ledger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "03a9a2ca-29e4-4a3d-b260-bd4fcaf2e40e",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

SMB Demo - Confirmed Time Entry to Slack. Uses httpRequest. Webhook trigger; 10 nodes.

Source: https://github.com/lh229470989/workforce-operations-copilot/blob/main/integrations/n8n/workflow-b-confirmed-slack.json — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

Flujowhatsapp. Uses @aldinokemal2104/n8n-nodes-gowa, postgres, httpRequest. Webhook trigger; 65 nodes.

@Aldinokemal2104/N8N Nodes Gowa, Postgres, HTTP Request
Slack & Telegram

HR teams, IT Operations, and System Administrators managing employee onboarding at scale. It’s perfect if you use Odoo 18 to trigger account requests and need Redmine + GitLab accounts created instant

HTTP Request, Slack
Slack & Telegram

This workflow is a complete, production-ready solution for recovering abandoned carts in Shopify stores using a multi-channel, multi-touch approach. It automates personalized follow-ups via Email, SMS

HTTP Request, Shopify, SendGrid +5
Slack & Telegram

WF_UNIFIED_LEGAL_AUTOMATION. Uses googleSheets, httpRequest, telegram, telegramTrigger. Webhook trigger; 53 nodes.

Google Sheets, HTTP Request, Telegram +1
Slack & Telegram

Backbrief: transcripts (Zoom webhook -> Slack + vault). Uses httpRequest, slack. Webhook trigger; 52 nodes.

HTTP Request, Slack