AutomationFlowsAI & RAG › Create Autonomous Payment Agents Using Agentgatepay and Multi-chain Tokens

Create Autonomous Payment Agents Using Agentgatepay and Multi-chain Tokens

ByAgentGatePay @agentgatepay on n8n.io

Get your AI agents paying for resources autonomously in under 10 minutes.

Event trigger★★★★☆ complexity27 nodesData TableHTTP Request
AI & RAG Trigger: Event Nodes: 27 Complexity: ★★★★☆ Added:
Create Autonomous Payment Agents Using Agentgatepay and Multi-chain Tokens — n8n workflow card showing Data Table, HTTP Request integration

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

This workflow follows the Datatable → HTTP Request recipe pattern — see all workflows that pair these two integrations.

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": "tgUy8qbiSc3uSHuX",
  "name": "\ud83e\udd16 AgentGatePay - Buyer Agent MCP [TEMPLATE]",
  "tags": [],
  "nodes": [
    {
      "id": "92e66d1d-f0f5-4d1e-96cd-aec1f1916e7d",
      "name": "\u25b6\ufe0f START",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        608,
        336
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5d3971b4-3854-40d0-b967-51c245a8dec1",
      "name": "1\ufe0f\u20e3 Load Config",
      "type": "n8n-nodes-base.code",
      "position": [
        816,
        336
      ],
      "parameters": {
        "jsCode": "const CONFIG = {\n  buyer: {\n    name: \"Research Assistant AI\",\n    company: \"Your Company\",\n    email: \"user@example.com\",  // \u26a0\ufe0f REPLACE: Your buyer agent email\n    task: \"Analyze top 5 competitors in SaaS market\",\n    api_key: \"YOUR_AGENTGATEPAY_API_KEY\",  // \u26a0\ufe0f REPLACE: From AgentGatePay signup\n    budget_usd: 100,\n    mandate_ttl_days: 7,\n    mandate_scope: \"resource.read payment.execute\"\n  },\n  seller: {\n    name: \"DataBot Pro\",\n    company: \"MarketInsights AI Ltd.\",\n    email: \"user@example.com\",\n    service: \"Premium Market Research Reports\",\n    api_url: \"https://YOUR_N8N.app.n8n.cloud/webhook/seller-resource-api-test\",  // \u26a0\ufe0f REPLACE: Seller webhook URL\n    selected_resource_id: \"saas-competitors-2025\"\n  },\n  render: {\n    service_url: \"https://YOUR_RENDER_APP.onrender.com\"\n  },\n  blockchain: {\n    chain: \"base\",\n    token: \"USDC\",\n    rpc_url: \"https://mainnet.base.org\"\n  },\n  agentgatepay: {\n    api_url: \"https://api.agentgatepay.com\",\n    mcp_endpoint: \"https://mcp.agentgatepay.com\"\n  }\n};\n\nreturn [{json: {config: CONFIG, session: {id: `session_${Date.now()}`, started_at: new Date().toISOString(), agent: CONFIG.buyer.email}}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f9512e04-334e-4d81-9e99-90c3d89c9a3f",
      "name": "2\ufe0f\u20e3 \ud83d\udcca Get Mandate Token",
      "type": "n8n-nodes-base.dataTable",
      "notes": "\ud83d\udd0d CHECK: Does mandate exist?\n\n\u26a0\ufe0f CONFIGURE: You MUST select 'AgentPay_Mandates' from dropdown!\n\nClick this node \u2192 Data table dropdown \u2192 Select AgentPay_Mandates \u2192 Save",
      "position": [
        1024,
        336
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "0RVPG9hWFRYnyUw7",
          "cachedResultUrl": "/projects/GLZPTknscCLLXTgj/datatables/0RVPG9hWFRYnyUw7",
          "cachedResultName": "AgentPay_Mandates"
        }
      },
      "typeVersion": 1,
      "continueOnFail": true,
      "alwaysOutputData": true
    },
    {
      "id": "64fe4d58-91f7-41fd-a5a0-87ec24b44e6d",
      "name": "2B\ufe0f\u20e3 Normalize Result",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udd04 NORMALIZE: Handle empty table\n\nIf table empty \u2192 Pass { mandate_token: null }\nIf table has row \u2192 Pass the row data\n\nThis ensures Node 3 always receives data!",
      "position": [
        1232,
        336
      ],
      "parameters": {
        "jsCode": "// Normalize Data Table output\n// When table is empty, create empty object\n// When table has row, pass it through\n\nconst config = $('1\ufe0f\u20e3 Load Config').first().json.config;\n\nif ($input.all().length === 0) {\n  // Table is empty - return EMPTY STRING (not null)\n  console.log('\ud83d\udcca Data Table is empty - returning empty string');\n  return [{ json: { mandate_token: '', config: config } }];\n}\n\n// Table has data - pass it through with config\nconst allRows = $input.all();\nconsole.log(`\ud83d\udcca Data Table returned ${allRows.length} row(s)`);\n\nif (allRows.length > 1) {\n  console.warn('\u26a0\ufe0f WARNING: Multiple rows found! Using first row only.');\n}\n\nconst tableData = $input.first().json;\nconsole.log(`   mandate_token: ${tableData.mandate_token ? tableData.mandate_token.substring(0, 30) + '...' : 'EMPTY/NULL'}`);\n\nreturn [{ json: { ...tableData, config: config } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "2b833bcc-d396-4680-b313-0a1d4941cb74",
      "name": "3\ufe0f\u20e3 Has Token?",
      "type": "n8n-nodes-base.if",
      "notes": "\ud83d\udd00 ROUTER:\n\nTRUE \u2192 Token exists \u2192 Verify it\nFALSE \u2192 No token \u2192 Create new mandate",
      "position": [
        1440,
        336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-token",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.mandate_token }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "09934b2c-4f43-4d94-9952-93f22fd20353",
      "name": "4\ufe0f\u20e3 \u2705 Verify Existing Token",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "\ud83d\udd12 ASK GATEWAY: Is this token still valid?\n\nGateway checks:\n\u2705 Signature valid?\n\u2705 Not expired?\n\u2705 Budget remaining?\n\u2705 Scope correct?\n\nGateway = Source of Truth!",
      "position": [
        1696,
        192
      ],
      "parameters": {
        "url": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.agentgatepay.mcp_endpoint }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"jsonrpc\": \"2.0\",\n  \"id\": 3,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"agentpay_verify_mandate\",\n    \"arguments\": {\n      \"mandate_token\": \"{{ $json.mandate_token }}\"\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9e9e0ede-81f8-472b-9364-5069045cfbf3",
      "name": "4B\ufe0f\u20e3 Check Verification",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udd0d PARSE GATEWAY RESPONSE:\n\n\u2705 Valid \u2192 Continue with existing mandate\n\u274c Invalid \u2192 ERROR with clear renewal instructions\n\nNEVER auto-renews! Security by design!",
      "position": [
        1904,
        192
      ],
      "parameters": {
        "jsCode": "const verify_response = $input.first().json;\nconst config = $('1\ufe0f\u20e3 Load Config').first().json.config;\nconst stored_token = $('2\ufe0f\u20e3 \ud83d\udcca Get Mandate Token').first().json.mandate_token;\n\n// Parse MCP response\nif (!verify_response.result || !verify_response.result.content) {\n  throw new Error('\u274c Gateway verification failed - invalid response');\n}\n\nconst text = verify_response.result.content[0].text;\nconst verification = JSON.parse(text);\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// \ud83d\udea8 MANDATE EXPIRED OR INVALID - SHOW CLEAR INSTRUCTIONS\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nif (!verification.valid) {\n  const errorMsg = `\n\ud83d\udea8 MANDATE EXPIRED OR INVALID!\n\n${verification.error || 'Mandate is no longer valid'}\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udccb TO CREATE NEW MANDATE (2 STEPS):\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n1\ufe0f\u20e3 Delete the token from storage:\n   \u2022 Go to: Data \u2192 AgentPay_Mandates\n   \u2022 Click the row\n   \u2022 Click Delete (trash icon)\n   \u2022 Click Confirm\n\n2\ufe0f\u20e3 Run this workflow again:\n   \u2022 Will automatically create fresh mandate\n   \u2022 New $${config.buyer.budget_usd} budget allocated\n   \u2022 7-day validity period\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n\ud83d\udca1 WHY THIS HAPPENS:\nMandates have limited budget/time by design.\nThis is a SECURITY FEATURE to prevent unlimited spending.\n\nWhen budget depleted or time expired, you must\nmanually approve a new mandate.\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n  `;\n  \n  throw new Error(errorMsg);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// \u2705 MANDATE VALID - CONTINUE WITH EXISTING TOKEN\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconsole.log('\u267b\ufe0f REUSING EXISTING MANDATE!');\nconsole.log(`   Budget Remaining: $${verification.budget_remaining}`);\nconsole.log(`   TTL Remaining: ${verification.ttl_remaining_hours} hours`);\nconsole.log(`   Status: ${verification.status}`);\n\nconst mandate = {\n  token: stored_token,\n  id: verification.mandate_id || 'unknown',\n  budget_remaining: verification.budget_remaining,\n  ttl_hours: verification.ttl_remaining_hours,\n  status: verification.status\n};\n\nreturn [{\n  json: {\n    config,\n    mandate,\n    verification,\n    was_reused: true,\n    source: 'existing_token',\n    message: `\u267b\ufe0f REUSING MANDATE!\\n\\nBudget: $${verification.budget_remaining}\\nTTL: ${verification.ttl_remaining_hours} hours\\nStatus: ${verification.status}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "075f1847-da02-4a80-907a-0c978ea1d619",
      "name": "5\ufe0f\u20e3 \ud83c\udd95 Create New Mandate",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "\ud83c\udd95 CREATE: New mandate via Gateway API\n\nOnly runs when:\n- First time (no token in table)\n- OR user deleted token (manual renewal)\n\nNEVER auto-creates on expiry!",
      "position": [
        1696,
        480
      ],
      "parameters": {
        "url": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.agentgatepay.mcp_endpoint }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"agentpay_issue_mandate\",\n    \"arguments\": {\n      \"subject\": \"{{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.email }}\",\n      \"budget_usd\": {{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.budget_usd }},\n      \"scope\": \"{{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.mandate_scope }}\",\n      \"ttl_minutes\": {{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.mandate_ttl_days * 1440 }}\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8ae0aef5-0581-45f9-8aed-ee291f01fe6c",
      "name": "5B\ufe0f\u20e3 Parse New Mandate",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udce6 EXTRACT: Token from API response\n\nNext: VERIFY this new token works before saving!",
      "position": [
        1904,
        480
      ],
      "parameters": {
        "jsCode": "const api_response = $input.first().json;\nconst config = $('1\ufe0f\u20e3 Load Config').first().json.config;\n\nif (!api_response.result || !api_response.result.content) {\n  throw new Error('\u274c Failed to create mandate - invalid API response');\n}\n\nconst text = api_response.result.content[0].text;\nconst result = JSON.parse(text);\n\nif (!result.mandate_token) {\n  throw new Error('\u274c API did not return mandate_token');\n}\n\nconsole.log('\ud83c\udd95 NEW MANDATE CREATED!');\nconsole.log(`   Mandate ID: ${result.mandate_id}`);\nconsole.log(`   Token: ${result.mandate_token.substring(0, 20)}...`);\nconsole.log(`   Budget: $${result.budget_remaining || config.buyer.budget_usd}`);\nconsole.log(`   Expires: ${result.expires_at}`);\nconsole.log('   \u2705 Will be saved to table');\n\nconst mandate = {\n  token: result.mandate_token,\n  id: result.mandate_id,\n  budget_remaining: parseFloat(result.budget_remaining || config.buyer.budget_usd),\n  expires_at: result.expires_at\n};\n\nreturn [{\n  json: {\n    config,\n    mandate,\n    verification: {\n      valid: true,\n      budget_remaining: mandate.budget_remaining,\n      status: 'active'\n    },\n    was_reused: false,\n    source: 'new_mandate',\n    message: `\ud83c\udd95 NEW MANDATE!\\n\\nID: ${mandate.id}\\nBudget: $${mandate.budget_remaining}\\nExpires: ${mandate.expires_at}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c698bfa3-31df-4d15-b853-fbae8b6dc95f",
      "name": "6\ufe0f\u20e3 \u2705 Verify New Mandate",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "\ud83d\udd12 VERIFY: New mandate works!\n\nDon't save broken tokens to table.\nGateway confirms it's valid before we store it.",
      "position": [
        2112,
        480
      ],
      "parameters": {
        "url": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.agentgatepay.mcp_endpoint }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"jsonrpc\": \"2.0\",\n  \"id\": 4,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"agentpay_verify_mandate\",\n    \"arguments\": {\n      \"mandate_token\": \"{{ $json.mandate.token }}\"\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "={{ $('1\ufe0f\u20e3 Load Config').first().json.config.buyer.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e6648b7b-da08-4471-a8c9-d3022cb6dae5",
      "name": "6B\ufe0f\u20e3 Check New Mandate",
      "type": "n8n-nodes-base.code",
      "notes": "\u2705 CHECK: New mandate is valid\n\nIf invalid \u2192 ERROR (don't save)\nIf valid \u2192 Prepare token for saving",
      "position": [
        2320,
        480
      ],
      "parameters": {
        "jsCode": "const verify_response = $input.first().json;\nconst parsed_data = $('5B\ufe0f\u20e3 Parse New Mandate').first().json;\nconst config = $('1\ufe0f\u20e3 Load Config').first().json.config;\n\n// Parse MCP response\nif (!verify_response.result || !verify_response.result.content) {\n  throw new Error('\u274c Gateway verification failed - invalid response');\n}\n\nconst text = verify_response.result.content[0].text;\nconst verification = JSON.parse(text);\n\nif (!verification.valid) {\n  throw new Error(`\u274c New mandate is INVALID: ${verification.error || 'Unknown error'}`);\n}\n\nconst token = parsed_data.mandate.token;\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// \ud83d\udee1\ufe0f STRICT VALIDATION - PREVENT NULL/INVALID TOKENS\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Check 1: Exists and is a string\nif (!token || typeof token !== 'string') {\n  throw new Error('\u274c BLOCKED: Token is missing or not a string!');\n}\n\n// Check 2: Not literal 'null' or 'undefined' strings\nif (token === 'null' || token === 'undefined' || token === '') {\n  throw new Error('\u274c BLOCKED: Token is null, undefined, or empty string!');\n}\n\n// Check 3: Minimum length (JWT tokens are 100+ chars)\nif (token.length < 50) {\n  throw new Error(`\u274c BLOCKED: Token too short (${token.length} chars) - likely invalid!`);\n}\n\n// Check 4: Must contain periods (JWT format: header.payload.signature)\nif (!token.includes('.')) {\n  throw new Error('\u274c BLOCKED: Token is not in JWT format (missing periods)!');\n}\n\nconsole.log('\u2705 New mandate verified successfully!');\nconsole.log(`   Token: ${token.substring(0, 30)}...`);\nconsole.log(`   Token length: ${token.length} chars`);\nconsole.log(`   Budget: $${verification.budget_remaining}`);\nconsole.log(`   Status: ${verification.status}`);\nconsole.log('   \ud83d\udee1\ufe0f Validation: ALL CHECKS PASSED');\n\n// Prepare for saving to table\nreturn [{\n  json: {\n    mandate_token: token,\n    _original_data: parsed_data  // Keep for Node 7B\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "18397ef6-45a5-49ba-9f8a-af2af960f664",
      "name": "7\ufe0f\u20e3 \ud83d\udcbe Insert Token",
      "type": "n8n-nodes-base.dataTable",
      "notes": "\ud83d\udcbe INSERT: Verified token to table\n\nReceives: { mandate_token: \"...\" }\nAuto-maps to table column\n\nOnly saves tokens that passed verification!\n\n\u26a0\ufe0f IMPORTANT: RE-SELECT 'AgentPay_Mandates' from dropdown!",
      "position": [
        2528,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "mandate_token": "={{ $json.mandate_token }}"
          },
          "schema": [
            {
              "id": "mandate_token",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "mandate_token",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "0RVPG9hWFRYnyUw7",
          "cachedResultUrl": "/projects/GLZPTknscCLLXTgj/datatables/0RVPG9hWFRYnyUw7",
          "cachedResultName": "AgentPay_Mandates"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ce95fef9-8a7c-44d4-a56f-80d2b8a2054c",
      "name": "7B\ufe0f\u20e3 Restore Data",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udd04 RESTORE: Original data structure\n\nNode 7 returns table response\nNode 8 needs original mandate data\n\nThis bridges the gap.",
      "position": [
        2720,
        480
      ],
      "parameters": {
        "jsCode": "// Restore original data for merge node\nconst table_response = $input.first().json;\nconst original_data = $('6B\ufe0f\u20e3 Check New Mandate').first().json._original_data;\n\nconsole.log('\u2705 Token saved to table successfully!');\n\n// Return original mandate data for Node 8\nreturn [{ json: original_data }];"
      },
      "typeVersion": 2
    },
    {
      "id": "98ce0832-7986-4700-9731-eebdf91742bd",
      "name": "8\ufe0f\u20e3 \ud83d\udd00 Merge Paths",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udd00 MERGE: Both paths converge here\n\nPath 1: Verified existing token \u267b\ufe0f\nPath 2: Created new token \ud83c\udd95\n\nBoth continue with same flow",
      "position": [
        2112,
        192
      ],
      "parameters": {
        "jsCode": "// Both paths merge here (existing token or new token)\nconst data = $input.first().json;\n\nconst config = data.config;\nconst session = $('1\ufe0f\u20e3 Load Config').first().json.session;\nconst mandate = data.mandate;\nconst verification = data.verification;\n\nconst merchant = config.seller;\n\nconst selected_resource = {\n  id: merchant.selected_resource_id,\n  title: \"SaaS Competitor Analysis 2025\",\n  description: \"Top 5 competitors analysis\"\n};\n\nconst resource_request = {\n  from: config.buyer.email,\n  to: merchant.email,\n  resource_id: selected_resource.id,\n  resource_title: selected_resource.title,\n  mandate_token: mandate.token,\n  timestamp: new Date().toISOString()\n};\n\nreturn [{\n  json: {\n    config,\n    session,\n    mandate,\n    verification,\n    was_reused: data.was_reused,\n    merchant: merchant,\n    selected_resource: selected_resource,\n    resource_request: resource_request,\n    message: `\u2705 Mandate Ready!\\n\\nSource: ${data.was_reused ? 'Reused existing \u267b\ufe0f' : 'Created new \ud83c\udd95'}\\nBudget: $${verification.budget_remaining}\\n\\nMerchant: ${merchant.name}\\nResource: ${selected_resource.title}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "8d0a44b0-efe9-48ed-9e92-3c08f8741e35",
      "name": "9\ufe0f\u20e3 Request Resource (x402)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2320,
        192
      ],
      "parameters": {
        "url": "={{ $json.config.seller.api_url }}/resource/{{ $json.config.seller.selected_resource_id }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "fullResponse": true
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-agent-id",
              "value": "={{ $json.config.buyer.email }}"
            },
            {
              "name": "x-mandate",
              "value": "={{ $json.mandate.token }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "187810bc-9ee3-4fdb-8577-338dd8e45d01",
      "name": "9B\ufe0f\u20e3 Parse 402 Response",
      "type": "n8n-nodes-base.code",
      "position": [
        2528,
        192
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst body = response.body || response;\n\nif (response.statusCode !== 402 && body.statusCode !== 402) {\n  throw new Error(`Expected 402 Payment Required, got ${response.statusCode || body.statusCode}`);\n}\n\nconst payment_required = body;\nconst data = $('8\ufe0f\u20e3 \ud83d\udd00 Merge Paths').first().json;\n\nreturn [{\n  json: {\n    ...data,\n    payment_required: payment_required,\n    message: `\ud83d\udcb0 402 Received!\\n\\nWallet: ${payment_required.payTo}\\nAmount: $${payment_required.priceUsd}\\nToken: ${payment_required.token}\\nChain: ${payment_required.chain}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d75a575f-995e-4dca-82b3-c48e7b095257",
      "name": "\ud83d\udd1f \ud83d\udd12 Sign Payment (Render)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2720,
        192
      ],
      "parameters": {
        "url": "={{ $('8\ufe0f\u20e3 \ud83d\udd00 Merge Paths').item.json.config.render.service_url }}/sign-payment",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"merchant_address\": \"{{ $json.payment_required.payTo }}\",\n  \"total_amount\": \"{{ $json.payment_required.amount }}\",\n  \"token\": \"{{ $json.payment_required.token }}\",\n  \"chain\": \"{{ $json.payment_required.chain }}\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "={{ $('8\ufe0f\u20e3 \ud83d\udd00 Merge Paths').item.json.config.buyer.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2eab8b07-a05e-4391-9ec1-2b5c3a1c9697",
      "name": "1\ufe0f\u20e31\ufe0f\u20e3 Extract TX Hashes",
      "type": "n8n-nodes-base.code",
      "position": [
        2928,
        192
      ],
      "parameters": {
        "jsCode": "const render_response = $input.first().json;\nconst data = $('9B\ufe0f\u20e3 Parse 402 Response').first().json;\n\nif (!render_response.success) {\n  throw new Error(`Render signing failed: ${render_response.error || 'Unknown error'}`);\n}\n\nif (!render_response.tx_hash || !render_response.tx_hash_commission) {\n  throw new Error('Missing transaction hashes from Render');\n}\n\nreturn [{\n  json: {\n    ...data,\n    payment_proof: {\n      tx_hash: render_response.tx_hash,\n      tx_hash_commission: render_response.tx_hash_commission,\n      status: 'confirmed',\n      from: render_response.from,\n      merchant: data.payment_required.payTo,\n      commission_address: render_response.commission_address,\n      total_usd: render_response.total_usd,\n      merchant_usd: render_response.merchant_usd,\n      commission_usd: render_response.commission_usd,\n      token: data.payment_required.token,\n      chain: data.payment_required.chain,\n      merchant_url: `https://basescan.org/tx/${render_response.tx_hash}`,\n      commission_url: `https://basescan.org/tx/${render_response.tx_hash_commission}`\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "1aad75be-ca5b-447a-8b5f-6907b369ecde",
      "name": "1\ufe0f\u20e32\ufe0f\u20e3 Submit Payment (MCP)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3136,
        192
      ],
      "parameters": {
        "url": "={{ $json.config.agentgatepay.mcp_endpoint }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"jsonrpc\": \"2.0\",\n  \"id\": 2,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"agentpay_submit_payment\",\n    \"arguments\": {\n      \"mandate_token\": \"{{ $json.mandate.token }}\",\n      \"tx_hash\": \"{{ $json.payment_proof.tx_hash }}\",\n      \"tx_hash_commission\": \"{{ $json.payment_proof.tx_hash_commission }}\",\n      \"chain\": \"{{ $json.payment_required.chain }}\",\n      \"token\": \"{{ $json.payment_required.token }}\",\n      \"price_usd\": \"{{ $json.payment_required.priceUsd }}\"\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "={{ $('8\ufe0f\u20e3 \ud83d\udd00 Merge Paths').first().json.config.buyer.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7a2ff1f9-bf4a-425a-ba93-125ec6c95ed3",
      "name": "1\ufe0f\u20e33\ufe0f\u20e3 Receive Resource",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3344,
        192
      ],
      "parameters": {
        "url": "={{ $('9B\ufe0f\u20e3 Parse 402 Response').first().json.config.seller.api_url }}/resource/{{ $('9B\ufe0f\u20e3 Parse 402 Response').first().json.config.seller.selected_resource_id }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-payment",
              "value": "={{ $('1\ufe0f\u20e31\ufe0f\u20e3 Extract TX Hashes').first().json.payment_proof.tx_hash }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "cec5d7b2-90e4-4d84-81cc-cf4d6f33a308",
      "name": "1\ufe0f\u20e34\ufe0f\u20e3 Complete Task",
      "type": "n8n-nodes-base.code",
      "position": [
        3552,
        192
      ],
      "parameters": {
        "jsCode": "const resource_http_response = $input.first().json;\nconst payment_data = $('1\ufe0f\u20e31\ufe0f\u20e3 Extract TX Hashes').first().json;\nconst merge_data = $('8\ufe0f\u20e3 \ud83d\udd00 Merge Paths').first().json;\n\nconst resource_body = resource_http_response.body || resource_http_response;\nlet resource_data = typeof resource_body === 'object' ? resource_body : { raw: resource_body };\n\nconst spent = parseFloat(payment_data.payment_required.priceUsd);\nconst budget_before = merge_data.verification.budget_remaining;\nconst budget_after = budget_before - spent;\n\nreturn [{\n  json: {\n    config: merge_data.config,\n    session: merge_data.session,\n    resource_data: resource_data,\n    resource_http_status: resource_http_response.statusCode,\n    payment_proof: payment_data.payment_proof,\n    summary: {\n      task: merge_data.config.buyer.task,\n      status: \"COMPLETED\",\n      mandate_source: merge_data.was_reused ? \"Reused (\u267b\ufe0f)\" : \"Created New (\ud83c\udd95)\",\n      mandate_storage: \"Data Tables (1 column!)\",\n      budget_before_payment: budget_before,\n      budget_spent: spent,\n      budget_remaining: budget_after,\n      merchant_tx: payment_data.payment_proof.tx_hash,\n      commission_tx: payment_data.payment_proof.tx_hash_commission,\n      completed_at: new Date().toISOString()\n    },\n    message: `\ud83c\udf89 TASK COMPLETED !\\n\\n\u2705 Task: \"${merge_data.config.buyer.task}\"\\n\u2705 Resource Received (HTTP ${resource_http_response.statusCode})\\n\\n\ud83d\udd12 Mandate: ${merge_data.was_reused ? 'REUSED \u267b\ufe0f' : 'CREATED NEW \ud83c\udd95'}\\n\ud83d\udcca Storage: Data Tables (just 1 column!)\\n\\n\ud83d\udcb0 Budget:\\n   - Before: $${budget_before}\\n   - Spent: $${spent}\\n   - After: $${budget_after}\\n\\n\ud83d\udd17 TX: ${payment_data.payment_proof.tx_hash}\\n\\n\u2705 v3.7 = ULTRA SIMPLE!\\n   - 1 column table (not 9!)\\n   - Gateway is source of truth\\n   - Clear error messages\\n   - No auto-renewal (security!)\\n   - Easy manual renewal (delete row)`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "77711363-6bea-4837-8ee9-aa49bbe04633",
      "name": "START HERE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 520,
        "height": 600,
        "content": "# Buyer Agent Workflow\n\n**What it does:** AI agent that buys resources from sellers using AgentGatePay. Automatically reuses mandate tokens to track spending.\n\n**Quick setup (2 min):**\n1. Create Data Table called `AgentPay_Mandates` with one column: `mandate_token` (type: String).\n2. Choose blockchain: coin and network\n3. Click Node 2 and Node 7, re-select the table from dropdowns\n4. Edit Node 1 with your email, AgentGatePay API Key,seller URL, budget ($100 default) and Render URL or other external TX service for signing transactions.\n\n**How it works:**\n- First run creates a mandate token and saves it to the table\n- Next runs reuse the same token\n- Budget decreases with each payment\n- When budget's gone, delete the table row and run again for a fresh mandate\n\n**Cost:** $0.01 per resource \u00b7 Budget tracked automatically \u00b7 No surprises\n\nFor more info:\nhttps://github.com/AgentGatePay/agentgatepay-examples/tree/main/n8n"
      },
      "typeVersion": 1
    },
    {
      "id": "49fe3a6c-4c97-4c7a-b5a4-764da49d2866",
      "name": "Sticky Note 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 280,
        "content": "## Mandate Management\n\nLoads your config, checks if you have an existing mandate token in the Data Table. If found, verify it. If not, create a new one."
      },
      "typeVersion": 1
    },
    {
      "id": "3c1b7cc0-e7ab-466e-abf8-71b752011527",
      "name": "Sticky Note 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 418,
        "height": 280,
        "content": "## Token Verification\n\nAsks AgentGatePay if your existing token is still valid. Checks signature, expiry, and remaining budget. If expired, you'll get clear instructions to renew."
      },
      "typeVersion": 1
    },
    {
      "id": "f42cea23-beb7-4fd2-8a63-b90ed0e7adb6",
      "name": "Sticky Note 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 1228,
        "height": 280,
        "content": "## New Mandate Creation\n\nCreates fresh mandate via Gateway API, verifies it works, validates the token format, then saves to Data Table. Only runs when you don't have a valid token."
      },
      "typeVersion": 1
    },
    {
      "id": "368ec38d-61d3-4a32-aab6-f1fe07d7aa2e",
      "name": "Sticky Note 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 952,
        "height": 280,
        "content": "## Payment Flow\n\nMerges both paths (reused or new token), requests the resource, receives 402 payment details, signs the payment via Render service, extracts transaction hashes."
      },
      "typeVersion": 1
    },
    {
      "id": "d0ca8152-ff18-4414-b214-72c47dad9d19",
      "name": "Sticky Note 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3072,
        64
      ],
      "parameters": {
        "color": 7,
        "width": 648,
        "height": 280,
        "content": "## Resource Delivery\n\nSubmits payment proof to Gateway, receives the paid resource from seller, calculates updated budget, and completes the task with full summary."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "62f2c498-0f5e-4d72-a9b5-1ec08961a0d5",
  "connections": {
    "\u25b6\ufe0f START": {
      "main": [
        [
          {
            "node": "1\ufe0f\u20e3 Load Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3\ufe0f\u20e3 Has Token?": {
      "main": [
        [
          {
            "node": "4\ufe0f\u20e3 \u2705 Verify Existing Token",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "5\ufe0f\u20e3 \ud83c\udd95 Create New Mandate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1\ufe0f\u20e3 Load Config": {
      "main": [
        [
          {
            "node": "2\ufe0f\u20e3 \ud83d\udcca Get Mandate Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7B\ufe0f\u20e3 Restore Data": {
      "main": [
        [
          {
            "node": "8\ufe0f\u20e3 \ud83d\udd00 Merge Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8\ufe0f\u20e3 \ud83d\udd00 Merge Paths": {
      "main": [
        [
          {
            "node": "9\ufe0f\u20e3 Request Resource (x402)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2B\ufe0f\u20e3 Normalize Result": {
      "main": [
        [
          {
            "node": "3\ufe0f\u20e3 Has Token?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7\ufe0f\u20e3 \ud83d\udcbe Insert Token": {
      "main": [
        [
          {
            "node": "7B\ufe0f\u20e3 Restore Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5B\ufe0f\u20e3 Parse New Mandate": {
      "main": [
        [
          {
            "node": "6\ufe0f\u20e3 \u2705 Verify New Mandate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6B\ufe0f\u20e3 Check New Mandate": {
      "main": [
        [
          {
            "node": "7\ufe0f\u20e3 \ud83d\udcbe Insert Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4B\ufe0f\u20e3 Check Verification": {
      "main": [
        [
          {
            "node": "8\ufe0f\u20e3 \ud83d\udd00 Merge Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9B\ufe0f\u20e3 Parse 402 Response": {
      "main": [
        [
          {
            "node": "\ud83d\udd1f \ud83d\udd12 Sign Payment (Render)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2\ufe0f\u20e3 \ud83d\udcca Get Mandate Token": {
      "main": [
        [
          {
            "node": "2B\ufe0f\u20e3 Normalize Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6\ufe0f\u20e3 \u2705 Verify New Mandate": {
      "main": [
        [
          {
            "node": "6B\ufe0f\u20e3 Check New Mandate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1\ufe0f\u20e33\ufe0f\u20e3 Receive Resource": {
      "main": [
        [
          {
            "node": "1\ufe0f\u20e34\ufe0f\u20e3 Complete Task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5\ufe0f\u20e3 \ud83c\udd95 Create New Mandate": {
      "main": [
        [
          {
            "node": "5B\ufe0f\u20e3 Parse New Mandate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9\ufe0f\u20e3 Request Resource (x402)": {
      "main": [
        [
          {
            "node": "9B\ufe0f\u20e3 Parse 402 Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd1f \ud83d\udd12 Sign Payment (Render)": {
      "main": [
        [
          {
            "node": "1\ufe0f\u20e31\ufe0f\u20e3 Extract TX Hashes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1\ufe0f\u20e31\ufe0f\u20e3 Extract TX Hashes": {
      "main": [
        [
          {
            "node": "1\ufe0f\u20e32\ufe0f\u20e3 Submit Payment (MCP)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4\ufe0f\u20e3 \u2705 Verify Existing Token": {
      "main": [
        [
          {
            "node": "4B\ufe0f\u20e3 Check Verification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1\ufe0f\u20e32\ufe0f\u20e3 Submit Payment (MCP)": {
      "main": [
        [
          {
            "node": "1\ufe0f\u20e33\ufe0f\u20e3 Receive Resource",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

Get your AI agents paying for resources autonomously in under 10 minutes.

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This is for SaaS founders, agency owners, and Sales Ops managers who use HubSpot but are tired of "toe-stepping." If your BDRs are accidentally emailing your AE’s active deals, or Marketing is blastin

Data Table, HTTP Request
AI & RAG

Who is this for? Agencies, consultants, and service providers who conduct discovery calls and need to quickly turn conversations into professional proposals.

Tool Think, Tool Calculator, Agent Tool +18
AI & RAG

Main. Uses httpRequest, agent, lmChatGoogleGemini, outputParserStructured. Event-driven trigger; 57 nodes.

HTTP Request, Agent, Google Gemini Chat +4
AI & RAG

This is an automated blog post generation system that: Researches topics using AI agents and web search tools Writes complete blog posts with proper SEO structure Generates custom images for each post

Output Parser Structured, Google Gemini Chat, HTTP Request Tool +11
AI & RAG

Receives campaign parameters via form, creates a Smartlead campaign, sources qualified leads through Wiza based on your ICP description, researches each prospect with Perplexity AI, generates personal

HTTP Request, Output Parser Structured, Memory Buffer Window +6