{
  "name": "SoftGlaze: create profiles from a Google Sheet (Beta)",
  "nodes": [
    {
      "parameters": {
        "content": "## Create SoftGlaze profiles from a Google Sheet (Beta)\n\nSheet columns: **name** (required), **group**, **proxyId**, **startUrl**, **notes**, **tags** (comma separated). One profile per row, one call per second. SoftGlaze generates each fingerprint.\n\n1. In SoftGlaze, create an **automation token** (Owner): Operate scope, limited to the target groups, with **create_profile** pre-approved. Without it, each row waits for your Allow in SoftGlaze.\n2. Use that token in the **Create the profile** node (Bearer Auth credential). Change `8080` if your port is different.\n3. Pick your sheet in **Read the sheet** (replace `CHANGE_ME`).\n4. Run it by hand and read the result.\n\nThe group must be one the token may use. More than 60 rows an hour go over the default limit and then ask you.\n\nBeta: not yet run against a live SoftGlaze. Guide: browser.softglaze.com/docs/ai-agents#n8n",
        "height": 340,
        "width": 520
      },
      "id": "879e5657-754c-422d-8ed3-93e0a3c34813",
      "name": "Read me",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -60,
        -360
      ]
    },
    {
      "parameters": {},
      "id": "96fc74dc-bdb1-4425-8034-8af59e3f5a65",
      "name": "Run it by hand",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "CHANGE_ME",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "Profiles",
          "mode": "name"
        },
        "options": {}
      },
      "id": "bfb3a0bc-aa03-4eb2-8b0f-ee86331b3079",
      "name": "Read the sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// One create_profile call per sheet row. Columns: name (required), group, proxyId,\n// startUrl, notes, tags (comma separated). Rows without a name are skipped.\nconst out = [];\nfor (const item of $input.all()) {\n  const r = item.json;\n  const name = String(r.name ?? '').trim();\n  if (!name) continue;\n  const args = { name };\n  if (String(r.group ?? '').trim()) args.group = /^\\d+$/.test(String(r.group).trim()) ? Number(r.group) : String(r.group).trim();\n  if (String(r.proxyId ?? '').trim()) args.proxyId = Number(r.proxyId);\n  if (String(r.startUrl ?? '').trim()) args.startUrl = String(r.startUrl).trim();\n  if (String(r.notes ?? '').trim()) args.notes = String(r.notes).trim();\n  if (String(r.tags ?? '').trim()) args.tags = String(r.tags).split(',').map((t) => t.trim()).filter(Boolean);\n  out.push({ json: { name, args } });\n}\nreturn out;"
      },
      "id": "27f89437-a2e8-485c-aac9-c3bf945eb975",
      "name": "Build the arguments",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://127.0.0.1:8080/api/v1/tools/create_profile",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.args) }}",
        "options": {
          "timeout": 130000,
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 1000
            }
          },
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "439e934c-c536-463c-b083-5da77426c6e7",
      "name": "Create the profile",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// One line per row: created, or why not.\nconst rows = $('Build the arguments').all();\nconst results = $input.all();\nlet created = 0;\nconst lines = results.map((r, i) => {\n  const name = rows[i] ? rows[i].json.name : '#' + (i + 1);\n  if (r.json.ok) { created += 1; return name + ': ' + r.json.summary; }\n  return name + ': not created. ' + (r.json.message || r.json.error || 'Unknown error');\n});\nreturn [{ json: { created, total: results.length, report: lines.join('\\n') } }];"
      },
      "id": "48114ba8-c6e0-4c6f-b1ae-d8e5145fe98e",
      "name": "Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        0
      ]
    }
  ],
  "connections": {
    "Run it by hand": {
      "main": [
        [
          {
            "node": "Read the sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read the sheet": {
      "main": [
        [
          {
            "node": "Build the arguments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build the arguments": {
      "main": [
        [
          {
            "node": "Create the profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create the profile": {
      "main": [
        [
          {
            "node": "Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
