AutomationFlowsData & Sheets › Manage Gym Check-ins and Subscriptions with Supabase, Google Sheets, and Qr…

Manage Gym Check-ins and Subscriptions with Supabase, Google Sheets, and Qr…

Original n8n title: Manage Gym Check-ins and Subscriptions with Supabase, Google Sheets, and Qr Codes

Byiamvaar @iamvaar on n8n.io

Youtube Video: https://youtu.be/QyIalCCy6s8?si=Zlmde_oZz3qbDGwJ Github Repo: https://github.com/blankarrayy/automated-gym-checkin-system

Webhook trigger★★★★★ complexity31 nodesN8N Nodes Barcode ScannerSupabase
Data & Sheets Trigger: Webhook Nodes: 31 Complexity: ★★★★★ Added:

This workflow corresponds to n8n.io template #16711 — we link there as the canonical source.

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
{
  "id": "qhFZ5grN9fdPR5Nw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Gym Checkin Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "3ec8c9d5-3693-4a24-9392-f897903f8364",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3536,
        -320
      ],
      "parameters": {
        "width": 480,
        "height": 1216,
        "content": "## Automated Gym Checkin System with unique QR code for each user checkin\n\n### How it works\n\n1. The workflow exposes multiple webhooks to manage gym check-ins, user registrations, and data updates.\n2. Two separate endpoints generate QR codes for checking in and for directing new users to the account registration link.\n3. Another endpoint handles database updates by modifying an existing Supabase record.\n4. The primary check-in endpoint validates incoming scans by fetching user status and history from Supabase.\n5. Depending on the validation logic output, a check-in record is created and specific success or error webhook responses are returned to the user.\n\n### Setup steps\n\n- [ ] Configure the Webhook nodes with appropriate authentication or IP restrictions to secure your endpoints.\n- [ ] Connect your Supabase account and configure the nodes with your specific project URL and API credentials.\n- [ ] Ensure the correct Supabase table names and operations are mapped in all database nodes.\n- [ ] Update the webhook response nodes to return the required structured payload matching your frontend requirements.\n\n### Customization\n\nThe validation logic inside the Code node can be customized to support different check-in rules, membership tiers, or daily scan limits.\n\n@[youtube](QyIalCCy6s8)"
      },
      "typeVersion": 1
    },
    {
      "id": "493c91e7-2988-45eb-97a1-a997357a7819",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2960,
        -320
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 304,
        "content": "## Handle user data update\n\nReceives webhook requests to update an existing user's data row in Supabase."
      },
      "typeVersion": 1
    },
    {
      "id": "de5aeaf1-07e3-477c-8280-97691992bc6c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2976,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 752,
        "height": 320,
        "content": "## Main check-in webhook\n\nTriggers on a check-in event, verifies initial request conditions, and fetches initial rows."
      },
      "typeVersion": 1
    },
    {
      "id": "3bf5da96-0e74-4902-8076-ca1c0f646d9c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2096,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 512,
        "content": "## Extract user ID\n\nValidates request integrity, extracts the logged-in user ID, or returns an early error webhook."
      },
      "typeVersion": 1
    },
    {
      "id": "beb7aec4-630a-4a61-9f0c-ff1eabf26daf",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1632,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Run validation logic\n\nFetches additional user records from the database and executes custom JavaScript validation logic."
      },
      "typeVersion": 1
    },
    {
      "id": "6089d371-61a8-4d46-8e26-afa1cad38bf9",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 400,
        "content": "## Save record and evaluate\n\nInserts the verified check-in record and evaluates the result to issue an early response if needed."
      },
      "typeVersion": 1
    },
    {
      "id": "906f43da-c9c7-4099-adf3-c772397e67a3",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        -432
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 512,
        "content": "## Final branching responses\n\nProcesses further logic branches to return specific success or failure webhook responses to the caller."
      },
      "typeVersion": 1
    },
    {
      "id": "e6e9be1c-a766-495c-a6b0-a28991e2ff43",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2976,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Generate check-in QR\n\nDedicated endpoint for creating and returning a QR code linked to the gym check-in process."
      },
      "typeVersion": 1
    },
    {
      "id": "0fb0d1c1-992e-4ed6-a80d-9a4e7c2a4e1d",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2960,
        704
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Generate registration QR\n\nDedicated endpoint for generating and returning a QR code that directs users to the new account registration page."
      },
      "typeVersion": 1
    },
    {
      "id": "dda1eeac-a3b2-4a98-b840-8eb15870a4b0",
      "name": "When Gym Checkin POSTed",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2928,
        160
      ],
      "parameters": {
        "path": "gymcheckin",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "4e28e251-3d8b-4354-951b-dbf67e33d6b3",
      "name": "Respond to Checkin Request",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        0,
        -272
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "text",
        "responseBody": "Entry Granted. Have a wonderful day"
      },
      "typeVersion": 1.5
    },
    {
      "id": "abd510bd-c52f-485a-97dc-19eff8078779",
      "name": "If Validation Passed",
      "type": "n8n-nodes-base.if",
      "position": [
        -672,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "405957de-d414-4c9e-8e6c-8d948205d1b9",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Validate Checkin Data').item.json.validation.userExists }}",
              "rightValue": false
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "b27a70ed-2913-4d89-9c18-e8eebda1c09b",
      "name": "If Checkin Validated",
      "type": "n8n-nodes-base.if",
      "position": [
        -224,
        -176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "288df27d-43ac-421a-a797-6a63e61ab167",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Validate Checkin Data').item.json.validation.allowedEntry }}",
              "rightValue": false
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "21343264-b591-452c-bc99-0ea787dbb1b7",
      "name": "Respond Success Full",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -448,
        16
      ],
      "parameters": {
        "options": {
          "responseCode": 401
        },
        "respondWith": "text",
        "responseBody": "No user found. Please use the QR code below to sign up"
      },
      "typeVersion": 1.5
    },
    {
      "id": "797431dc-39c9-48dc-bad0-d1ffcf1fa313",
      "name": "Respond Error Invalid",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        0,
        -80
      ],
      "parameters": {
        "options": {
          "responseCode": 401
        },
        "respondWith": "text",
        "responseBody": "You don't have a valid subscription. Please renew at reception"
      },
      "typeVersion": 1.5
    },
    {
      "id": "eaaf4c13-5598-4220-a4a0-c97d7c172af2",
      "name": "When QR Requested for Gym",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2928,
        496
      ],
      "parameters": {
        "path": "qr-for-gym-checkin",
        "options": {},
        "responseMode": "responseNode",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "0a207f1a-6ed9-4b9e-9b1b-a5f907c4b8fc",
      "name": "Respond with Gym QR",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -2480,
        496
      ],
      "parameters": {
        "options": {},
        "respondWith": "binary"
      },
      "typeVersion": 1.5
    },
    {
      "id": "e13a217c-7edb-4c92-a34f-0c223a326f98",
      "name": "When Account Created",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2912,
        -192
      ],
      "parameters": {
        "path": "account-creation",
        "options": {},
        "httpMethod": "POST",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "e2c52073-f4dd-4af4-8b3b-21e1d07b47a1",
      "name": "Generate Gym Checkin QR",
      "type": "n8n-nodes-barcode-scanner.barcodeScanner",
      "position": [
        -2704,
        496
      ],
      "parameters": {
        "text": "={{ 'https://combo.buildandcollab.com/webhook/gymcheckin?entry_id=e_' + $now.valueOf() + Math.random().toString(36).substring(2, 6) }}",
        "operation": "generate"
      },
      "typeVersion": 1
    },
    {
      "id": "6eb53283-06de-48ce-80d1-7ce72a436c5d",
      "name": "When QR Requested for Signup",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2912,
        832
      ],
      "parameters": {
        "path": "qr-for-new-account-registration",
        "options": {},
        "responseMode": "responseNode",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "5797dd38-d163-415a-a399-3f77cefd859a",
      "name": "Respond with Signup QR",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -2464,
        832
      ],
      "parameters": {
        "options": {},
        "respondWith": "binary"
      },
      "typeVersion": 1.5
    },
    {
      "id": "30e50d98-a4bf-456a-9119-dcfff9cf192b",
      "name": "Generate Signup QR",
      "type": "n8n-nodes-barcode-scanner.barcodeScanner",
      "position": [
        -2688,
        832
      ],
      "parameters": {
        "text": "gymcheckin.vercel.app",
        "operation": "generate"
      },
      "typeVersion": 1
    },
    {
      "id": "774c7e5c-b8dc-4034-89ff-7f4846f29915",
      "name": "Set User ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -1824,
        -80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "626a03ca-1594-4dca-b9f1-b3bf973ccb21",
              "name": "Userid",
              "type": "string",
              "value": "={{ $('When Gym Checkin POSTed').item.json.body.userid }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "03d942e6-be6f-46f1-8aa9-7b138c90740e",
      "name": "Validate Checkin Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -1136,
        -80
      ],
      "parameters": {
        "jsCode": "// Loop through all incoming items\nfor (const item of $input.all()) {\n  const json = item.json;\n  \n  // 1. Extract the values safely using the exact keys from the input\n  const userId = json.user_id; \n  const isSubscribed = json.is_subscribed;\n\n  // 2. Run the validation logic\n  const userExists = !!userId; \n  const hasActiveSubscription = isSubscribed === true; // Strict boolean check\n\n  // 3. Determine if they are allowed entry\n  const allowedEntry = userExists && hasActiveSubscription;\n\n  // 4. Add the results back to the item's JSON output\n  item.json.validation = {\n    userExists: userExists,\n    hasActiveSubscription: hasActiveSubscription,\n    allowedEntry: allowedEntry\n  };\n}\n\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "14404c56-f76b-48c0-b9da-6d7a292809d4",
      "name": "If Entry ID Provided",
      "type": "n8n-nodes-base.if",
      "position": [
        -2704,
        160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4c61df06-1693-4b3c-b744-10e60de9a6bd",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.query.entry_id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "fcee1deb-caeb-4bb2-99af-1075083f886f",
      "name": "If Entry ID Empty",
      "type": "n8n-nodes-base.if",
      "position": [
        -2048,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "6fb82cc4-5876-444b-a2d8-7896245be6f4",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.entry_id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "f83c93c3-31e0-4834-bda3-c60be5ae0d3b",
      "name": "Respond Missing Entry ID",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1824,
        112
      ],
      "parameters": {
        "options": {
          "responseCode": 409
        },
        "respondWith": "text",
        "responseBody": "This QR code has already been used. Please refresh the page to get a new QR code and scan again"
      },
      "typeVersion": 1.5
    },
    {
      "id": "76e2ad62-566a-4490-9cf0-920cba8a46e4",
      "name": "Fetch Entry Details",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -2368,
        144
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "entry_id",
              "keyValue": "={{ $json.query.entry_id }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "entry_logs",
        "operation": "getAll"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "4fa29059-daf7-4aee-8d39-0e6a6e3fad9f",
      "name": "Fetch User Data",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -1584,
        -80
      ],
      "parameters": {
        "tableId": "gym_sub",
        "operation": "getAll"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "bcbbcd14-922b-404b-97ce-3ecb836a97a3",
      "name": "Log Checkin in Sheets",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -912,
        -80
      ],
      "parameters": {
        "tableId": "entry_logs",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "entry_id",
              "fieldValue": "={{ $('When Gym Checkin POSTed').item.json.query.entry_id }}"
            },
            {
              "fieldId": "user_id",
              "fieldValue": "={{ $('When Gym Checkin POSTed').item.json.body.userid }}"
            },
            {
              "fieldId": "name",
              "fieldValue": "={{ $('Fetch User Data').item.json.name }}"
            },
            {
              "fieldId": "entry_request_time",
              "fieldValue": "={{ $now }}"
            },
            {
              "fieldId": "entry_status",
              "fieldValue": "={{ $json.validation.allowedEntry }}"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "eb7f4180-eff1-403a-97bc-06e992c46b7e",
      "name": "Update Account Status",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -2704,
        -192
      ],
      "parameters": {
        "tableId": "gym_sub",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "user_id",
              "fieldValue": "={{ $json.body.record.id }}"
            },
            {
              "fieldId": "name",
              "fieldValue": "={{ $json.body.record.raw_user_meta_data.full_name }}"
            },
            {
              "fieldId": "is_subscribed",
              "fieldValue": "FALSE"
            }
          ]
        },
        "operation": "update"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "77c2792f-c4c7-4dab-a5c4-25296b7b2d62",
  "nodeGroups": [],
  "connections": {
    "Set User ID": {
      "main": [
        [
          {
            "node": "Fetch User Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch User Data": {
      "main": [
        [
          {
            "node": "Validate Checkin Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Entry ID Empty": {
      "main": [
        [
          {
            "node": "Set User ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Missing Entry ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Signup QR": {
      "main": [
        [
          {
            "node": "Respond with Signup QR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Entry Details": {
      "main": [
        [
          {
            "node": "If Entry ID Empty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Checkin Validated": {
      "main": [
        [
          {
            "node": "Respond to Checkin Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Error Invalid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Entry ID Provided": {
      "main": [
        [
          {
            "node": "Fetch Entry Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Validation Passed": {
      "main": [
        [
          {
            "node": "If Checkin Validated",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Success Full",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Account Created": {
      "main": [
        [
          {
            "node": "Update Account Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Checkin in Sheets": {
      "main": [
        [
          {
            "node": "If Validation Passed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Checkin Data": {
      "main": [
        [
          {
            "node": "Log Checkin in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Gym Checkin QR": {
      "main": [
        [
          {
            "node": "Respond with Gym QR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Gym Checkin POSTed": {
      "main": [
        [
          {
            "node": "If Entry ID Provided",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When QR Requested for Gym": {
      "main": [
        [
          {
            "node": "Generate Gym Checkin QR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When QR Requested for Signup": {
      "main": [
        [
          {
            "node": "Generate Signup QR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

Youtube Video: https://youtu.be/QyIalCCy6s8?si=Zlmde_oZz3qbDGwJ Github Repo: https://github.com/blankarrayy/automated-gym-checkin-system

Source: https://n8n.io/workflows/16711/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

webhook - simulador PDV (fluxo). Uses supabase. Webhook trigger; 55 nodes.

Supabase
Data & Sheets

This workflow automates raw materials inventory management for businesses, eliminating manual stock updates, delayed material issue approvals, and missed low stock alerts. It ensures real-time stock t

Google Sheets, Gmail, Supabase +1
Data & Sheets

booking no duplicate. Uses httpRequest, supabase. Webhook trigger; 38 nodes.

HTTP Request, Supabase
Data & Sheets

Backend Erick. Uses supabase, n8n-nodes-evolution-api. Webhook trigger; 36 nodes.

Supabase, N8N Nodes Evolution Api
Data & Sheets

01-intake-storage. Uses airtable, supabase, stopAndError, httpRequest. Webhook trigger; 33 nodes.

Airtable, Supabase, Stop And Error +2