{
  "tool": "n8n",
  "format": "n8n-workflow",
  "artifact": {
    "name": "Sintr: lead to live site",
    "nodes": [
      {
        "id": "n1",
        "name": "New lead",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2,
        "position": [
          0,
          0
        ],
        "parameters": {
          "httpMethod": "POST",
          "path": "sintr-new-lead"
        }
      },
      {
        "id": "n2",
        "name": "Your AI step: write the brief",
        "type": "n8n-nodes-base.noOp",
        "typeVersion": 1,
        "position": [
          240,
          0
        ],
        "parameters": {},
        "notes": "Replace this node with your own AI node. Its output must be the brief JSON for the \"local-business\" template (fields from the template's brief JSON schema, such as businessName, phone, address.city), in a field called brief. Sintr provides no AI model. Use your AI step or your AI assistant to turn the lead into the brief JSON for the chosen template.",
        "notesInFlow": true
      },
      {
        "id": "n3",
        "name": "Sintr: create draft",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          480,
          0
        ],
        "parameters": {
          "method": "POST",
          "url": "https://dashboard.sintr.dev/api/automation/drafts",
          "sendHeaders": true,
          "headerParameters": {
            "parameters": [
              {
                "name": "Authorization",
                "value": "Bearer {{SINTR_TOKEN}}"
              },
              {
                "name": "Idempotency-Key",
                "value": "={{$('New lead').item.json.body.leadId}}"
              }
            ]
          },
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "={{ JSON.stringify({ clientRef: $('New lead').item.json.body.leadId, name: $('New lead').item.json.body.businessName, template: \"local-business\", brief: $json.brief }) }}"
        },
        "notes": "leadId and businessName come from the body of the \"New lead\" webhook; brief comes from your AI step.",
        "notesInFlow": true
      },
      {
        "id": "n4",
        "name": "Sintr events",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2,
        "position": [
          0,
          300
        ],
        "parameters": {
          "httpMethod": "POST",
          "path": "sintr-events",
          "options": {
            "rawBody": true
          }
        }
      },
      {
        "id": "n5",
        "name": "Verify signature",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          240,
          300
        ],
        "parameters": {
          "mode": "runOnceForAllItems",
          "language": "javaScript",
          "jsCode": "// Verifies the Sintr-Signature header. Expected inputs:\n//   require, headers, rawBody, secret, nowMs (see below for the n8n defaults).\nconst crypto = require('crypto');\nconst hdrs = typeof headers !== 'undefined' ? headers : $input.first().json.headers;\nconst body = typeof rawBody !== 'undefined' ? rawBody : Buffer.from($input.first().binary.data.data, 'base64').toString('utf8');\nconst key = typeof secret !== 'undefined' ? secret : \"{{SINTR_SIGNING_SECRET}}\";\nconst now = typeof nowMs !== 'undefined' ? nowMs : Date.now();\nconst m = /^t=(\\d+),v1=([0-9a-f]{64})$/.exec(String(hdrs['sintr-signature'] || ''));\nif (!m) throw new Error('Missing or malformed Sintr-Signature');\nif (Math.abs(Math.floor(now / 1000) - Number(m[1])) > 300) throw new Error('Sintr-Signature is too old');\nconst expected = crypto.createHmac('sha256', key).update(m[1] + '.' + body).digest('hex');\nif (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(m[2], 'hex'))) throw new Error('Bad Sintr signature');\n// Dedupe on the Sintr-Event-Id header downstream: rare re-sends can happen.\nconst event = JSON.parse(body);\nreturn [{ json: { ...event, eventId: hdrs['sintr-event-id'] } }];"
        },
        "notes": "Needs the built-in module crypto allowed in n8n (NODE_FUNCTION_ALLOW_BUILTIN=crypto). Dedupe on the Sintr-Event-Id header: a delivery can rarely be sent twice, so ignore an event id you have already handled.",
        "notesInFlow": true
      },
      {
        "id": "n6",
        "name": "Route by event",
        "type": "n8n-nodes-base.switch",
        "typeVersion": 3,
        "position": [
          480,
          300
        ],
        "parameters": {
          "rules": {
            "values": [
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "typeValidation": "strict"
                  },
                  "combinator": "and",
                  "conditions": [
                    {
                      "leftValue": "={{$json.type}}",
                      "rightValue": "site.draft_ready",
                      "operator": {
                        "type": "string",
                        "operation": "equals"
                      }
                    }
                  ]
                },
                "renameOutput": true,
                "outputKey": "site.draft_ready"
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "typeValidation": "strict"
                  },
                  "combinator": "and",
                  "conditions": [
                    {
                      "leftValue": "={{$json.type}}",
                      "rightValue": "site.build_failed",
                      "operator": {
                        "type": "string",
                        "operation": "equals"
                      }
                    }
                  ]
                },
                "renameOutput": true,
                "outputKey": "site.build_failed"
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "typeValidation": "strict"
                  },
                  "combinator": "and",
                  "conditions": [
                    {
                      "leftValue": "={{$json.type}}",
                      "rightValue": "form.submitted",
                      "operator": {
                        "type": "string",
                        "operation": "equals"
                      }
                    }
                  ]
                },
                "renameOutput": true,
                "outputKey": "form.submitted"
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "typeValidation": "strict"
                  },
                  "combinator": "and",
                  "conditions": [
                    {
                      "leftValue": "={{$json.type}}",
                      "rightValue": "site.draft_expired",
                      "operator": {
                        "type": "string",
                        "operation": "equals"
                      }
                    }
                  ]
                },
                "renameOutput": true,
                "outputKey": "site.draft_expired"
              }
            ]
          }
        },
        "notes": "The Verify signature step outputs the event itself, so the type is $json.type and the payload is $json.data.",
        "notesInFlow": true
      },
      {
        "id": "n7",
        "name": "Send preview",
        "type": "n8n-nodes-base.noOp",
        "typeVersion": 1,
        "position": [
          720,
          160
        ],
        "parameters": {},
        "notes": "Placeholder: email or message the client the preview link. The event data has previewUrl and previewPassword ($json.data.previewUrl).",
        "notesInFlow": true
      },
      {
        "id": "n11",
        "name": "Send the lead to your CRM",
        "type": "n8n-nodes-base.noOp",
        "typeVersion": 1,
        "position": [
          720,
          360
        ],
        "parameters": {},
        "notes": "Placeholder for form.submitted: send the lead to your CRM. The event data has fields, formId, page and submissionId ($json.data.fields).",
        "notesInFlow": true
      },
      {
        "id": "n12",
        "name": "Save the leads of an expired draft",
        "type": "n8n-nodes-base.noOp",
        "typeVersion": 1,
        "position": [
          720,
          460
        ],
        "parameters": {},
        "notes": "Placeholder for site.draft_expired: send the leads to your CRM before they are gone. The draft is deleted; its leads are in $json.data.leads.",
        "notesInFlow": true
      },
      {
        "id": "n8",
        "name": "Payment received",
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2,
        "position": [
          0,
          600
        ],
        "parameters": {
          "httpMethod": "POST",
          "path": "sintr-payment-received"
        }
      },
      {
        "id": "n9",
        "name": "Sintr: publish",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          240,
          600
        ],
        "parameters": {
          "method": "POST",
          "url": "=https://dashboard.sintr.dev/api/automation/drafts/{{$json.body.siteId}}/publish",
          "sendHeaders": true,
          "headerParameters": {
            "parameters": [
              {
                "name": "Authorization",
                "value": "Bearer {{SINTR_TOKEN}}"
              }
            ]
          },
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "={{ JSON.stringify({ domain: $json.body.domain }) }}"
        },
        "notes": "Payment is handled in your own billing tool (Stripe, GHL or similar). The body of the incoming payment webhook must carry the siteId from the draft_ready event (and optionally domain).",
        "notesInFlow": true
      },
      {
        "id": "n10",
        "name": "Note: dedupe",
        "type": "n8n-nodes-base.stickyNote",
        "typeVersion": 1,
        "position": [
          0,
          420
        ],
        "parameters": {
          "content": "Dedupe on the Sintr-Event-Id header: a delivery can rarely be sent twice, so ignore an event id you have already handled.",
          "width": 420,
          "height": 100
        }
      }
    ],
    "connections": {
      "New lead": {
        "main": [
          [
            {
              "node": "Your AI step: write the brief",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Your AI step: write the brief": {
        "main": [
          [
            {
              "node": "Sintr: create draft",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Sintr events": {
        "main": [
          [
            {
              "node": "Verify signature",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Verify signature": {
        "main": [
          [
            {
              "node": "Route by event",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Route by event": {
        "main": [
          [
            {
              "node": "Send preview",
              "type": "main",
              "index": 0
            }
          ],
          [],
          [
            {
              "node": "Send the lead to your CRM",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Save the leads of an expired draft",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Payment received": {
        "main": [
          [
            {
              "node": "Sintr: publish",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    "settings": {}
  },
  "notes": [
    "Import: in n8n choose Workflows, Import from file (or paste the JSON onto the canvas).",
    "Add ?org=<organisation id> to the Sintr URLs only if your token covers more than one organisation.",
    "Replace {{SINTR_TOKEN}} with an automation token, preferably keep it in an n8n credential or variable.",
    "Set the webhook URL in Sintr's automation settings to the \"Sintr events\" webhook URL; replace {{SINTR_SIGNING_SECRET}} with the signing secret.",
    "Dedupe on the Sintr-Event-Id header: a delivery can rarely be sent twice, so ignore an event id you have already handled.",
    "Sintr provides no AI model. Use your AI step or your AI assistant to turn the lead into the brief JSON for the chosen template."
  ]
}