Mon workflow 23

Avancé

Ceci est unInvoice Processing, Multimodal AIworkflow d'automatisation du domainecontenant 46 nœuds.Utilise principalement des nœuds comme If, Code, Merge, Stripe, Airtable. Automatisation complète des factures : Airtable, QuickBooks et Stripe

Prérequis
  • Clé API Stripe
  • Clé API Airtable
  • Peut nécessiter les informations d'identification d'authentification de l'API cible
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "id": "wnHdgRqker2FalaJ",
  "meta": {
    "instanceId": "1a54c41d9050a8f1fa6f74ca858828ad9fb97b9fafa3e9760e576171c531a787"
  },
  "name": "My workflow 23",
  "tags": [],
  "nodes": [
    {
      "id": "989dcbdc-5d15-45df-bb0e-db6049c537e2",
      "name": "Airtable Trigger",
      "type": "n8n-nodes-base.airtableTrigger",
      "position": [
        60,
        1780
      ],
      "parameters": {
        "baseId": {
          "__rl": true,
          "mode": "id",
          "value": "{YOUR_AIRTABLE_BASE_ID}"
        },
        "tableId": {
          "__rl": true,
          "mode": "id",
          "value": "{YOUR_AIRTABLE_TABLE_ID}"
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerField": "Created",
        "authentication": "airtableTokenApi",
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "45863a72-e832-448e-88e6-edb6ee1c8908",
      "name": "IF - Vérification du statut",
      "type": "n8n-nodes-base.if",
      "position": [
        1580,
        1580
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.Status }}",
              "value2": "Approved for Invoicing"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "43c615f1-777f-43da-8274-2b9ecce18aa0",
      "name": "IF - Le client existe-t-il ?",
      "type": "n8n-nodes-base.if",
      "position": [
        3020,
        1340
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.Id ?? \"\" }}",
              "operation": "isEmpty"
            }
          ]
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "2cb49f5e-08f7-46ea-afab-217e98908a14",
      "mode": "append",
      "name": "Fusionner le client QBO",
      "type": "n8n-nodes-base.merge",
      "position": [
        4320,
        1540
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "60920472-77b6-4243-89b0-f162f6e900d9",
      "name": "Stripe - Trouver un client",
      "type": "n8n-nodes-base.stripe",
      "position": [
        5800,
        1020
      ],
      "parameters": {
        "resource": "customer",
        "customerId": "={{ $('Search records').item.json['Stripe Customer ID'] || \"\"}}"
      },
      "typeVersion": 1
    },
    {
      "id": "bd0adf89-499a-40ee-9e37-648ae0aad30d",
      "name": "IF - Le client Stripe existe-t-il ?",
      "type": "n8n-nodes-base.if",
      "position": [
        7220,
        1160
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.DisplayName }}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ea27b7d1-4e6e-4620-b402-b0b8e0db436d",
      "name": "Stripe - Créer un client",
      "type": "n8n-nodes-base.stripe",
      "position": [
        7960,
        980
      ],
      "parameters": {
        "name": "={{ $json.DisplayName }}",
        "resource": "customer",
        "operation": "create",
        "additionalFields": {
          "email": "={{ $json.PrimaryEmailAddr.Address }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "901e1828-f5e5-4801-83a0-53fce11f4ea0",
      "mode": "append",
      "name": "Fusionner le client Stripe",
      "type": "n8n-nodes-base.merge",
      "position": [
        8520,
        1380
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4c99441c-1f72-49d6-b421-62e3c2c72b28",
      "name": "QuickBooks - Trouver un client",
      "type": "n8n-nodes-base.quickbooks",
      "position": [
        2360,
        1160
      ],
      "parameters": {
        "limit": 500,
        "filters": {
          "query": "=WHERE DisplayName = '{{ $json['Client Name'] }}'\n\n"
        },
        "operation": "getAll"
      },
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "497c8d82-1916-4ac6-acd2-7fdc561d3919",
      "name": "Créer une facture",
      "type": "n8n-nodes-base.quickbooks",
      "position": [
        13120,
        1420
      ],
      "parameters": {
        "Line": [
          {
            "Qty": "={{ $('Update Quickbooks and Stripe Customer Ids').item.json.fields.Quantity }}",
            "Amount": "={{ $json.Amount }}",
            "itemId": "={{ $json.Id }}",
            "DetailType": "SalesItemLineDetail",
            "Description": "={{ $json.Description }}"
          }
        ],
        "resource": "invoice",
        "operation": "create",
        "CustomerRef": "={{ $('Update Quickbooks and Stripe Customer Ids').item.json.fields['QuickBooks Customer ID'] }}",
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "b1330d78-6e36-4296-98b0-0312804e955d",
      "name": "Créer un client",
      "type": "n8n-nodes-base.quickbooks",
      "position": [
        3680,
        1100
      ],
      "parameters": {
        "operation": "create",
        "displayName": "={{ $('IF - Status Check').item.json['Client Name'] }}",
        "additionalFields": {
          "PrimaryEmailAddr": "={{ $('IF - Status Check').item.json['Client Email'] }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8b371200-572e-4995-b7d7-d496751342f4",
      "name": "Rechercher des enregistrements",
      "type": "n8n-nodes-base.airtable",
      "position": [
        800,
        1360
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_BASE_NAME}"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}/{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_TABLE_NAME}"
        },
        "options": {},
        "operation": "search"
      },
      "typeVersion": 2.1
    },
    {
      "id": "d56e433c-447b-406f-8467-a8c5de082d5b",
      "name": "Note autocollante",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -100,
        1920
      ],
      "parameters": {
        "color": 3,
        "width": 500,
        "height": 300,
        "content": "### Step 1: Airtable Trigger 🚦📋\n\nThis node triggers the workflow whenever there is a change in the **Created** column, effectively activating when new data is added.\n\nWhy this step is important:\n\n- ⏰ Automatically starts the workflow on new entries.\n- 📈 Monitors real-time changes for timely processing.\n- 🔄 Ensures your automation responds instantly to new Airtable data.\n\nIt’s the step that keeps your workflow synced with your Airtable updates. 🔔✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "44f16735-21cb-4a35-bb97-45bf8ec79469",
      "name": "Note autocollante1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        1040
      ],
      "parameters": {
        "width": 460,
        "height": 300,
        "content": "### Step 2: Airtable Search Records 🔍📋\n\nThis node searches and retrieves all records from a specific Airtable table.\n\nWhy this step is important:\n\n- 🔎 Gathers complete data from the table for processing.\n- 📊 Enables further filtering, updating, or analysis within the workflow.\n- 🗂️ Provides a snapshot of all relevant records at once.\n\nIt’s the step that collects your data foundation for the automation ahead. 🧱✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "430acba9-f9ad-424f-888e-a239968e9bbf",
      "name": "Note autocollante2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        1220
      ],
      "parameters": {
        "color": 5,
        "width": 500,
        "height": 300,
        "content": "### Step 3: Status Check (If Node) ✅❌\n\nThis node checks whether the **Status** field is set to **'Approved for Invoicing'**.\n\n- **True:** Continues with the workflow.\n- **False:** Gracefully exits the workflow.\n\nWhy this step is important:\n\n- ✔️ Ensures only approved records proceed for invoicing.\n- 🛑 Prevents processing of draft or already invoiced data.\n- 🔄 Maintains workflow efficiency and accuracy."
      },
      "typeVersion": 1
    },
    {
      "id": "5d2740d4-c4fc-4290-914c-bcbff9202b1c",
      "name": "Note autocollante3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2200,
        2080
      ],
      "parameters": {
        "color": 3,
        "width": 520,
        "height": 300,
        "content": "### Graceful Exit (No-Op Node) 🛑✨\n\nThis **No Operation** node acts as a graceful exit point for items whose status is **not** 'Approved for Invoicing'.\n\nWhy this step is important:\n\n- 🛡️ Prevents further processing of unapproved records.\n- 🔄 Ensures the workflow ends cleanly without errors.\n- 🧹 Keeps workflow logic clear and organized.\n\nIt’s the safe landing spot that quietly ends the flow when no action is needed. 🚪✅\n"
      },
      "typeVersion": 1
    },
    {
      "id": "12d6c762-e245-4203-b91d-a03ad0c39c5a",
      "name": "Quitter le flux de travail",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2400,
        1920
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "d3de759a-8382-4127-867f-84afbc9fb1b6",
      "name": "Note autocollante4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2180,
        800
      ],
      "parameters": {
        "color": 4,
        "width": 460,
        "height": 320,
        "content": "### Step 4: Find Customer in QuickBooks 🔍👤\n\nThis node uses the **Find Customer** operation to search for a customer in QuickBooks based on the **Display Name** parameter.\n\nWhy this step is important:\n\n- 🔎 Quickly locates existing customers by their display name.\n- ✅ Helps avoid duplicate customer records.\n- 📋 Ensures accurate linking of invoices and estimates to the right customer.\n\nIt’s the step that verifies customer existence to maintain clean and organized data. 🧹✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2aa200ff-342e-4a9b-b46d-956dc31ea3e1",
      "name": "Note autocollante5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2820,
        1480
      ],
      "parameters": {
        "color": 6,
        "width": 580,
        "height": 340,
        "content": "### Step 5: Customer Existence Check (If Node) ❓✅❌\n\nThis node checks whether a customer exists in QuickBooks.\n\n- **False:** Customer exists; workflow continues without creating a new customer.\n- **True:** Customer does **not** exist; triggers creation of a new customer before continuing.\n\nWhy this step is important:\n\n- 🔄 Directs the workflow to create a customer only when needed.\n- 🛡️ Prevents duplicate customer entries.\n- ⚙️ Ensures smooth, logical progression of the automation.\n\nIt’s the decision point that manages customer creation intelligently. 🔀✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "054ac6a9-7198-49d3-8005-8291e2b99de1",
      "name": "Note autocollante6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3480,
        780
      ],
      "parameters": {
        "color": 4,
        "width": 480,
        "height": 300,
        "content": "Create Customer in QuickBooks ➕👤\n\nThis node uses the **Create Customer** operation to add a new customer to QuickBooks using details from Airtable.\n\nWhy this step is important:\n\n- 🆕 Adds new customers automatically when they don’t exist.\n- 📥 Keeps your QuickBooks customer database current and accurate.\n- 🔗 Links customer records to subsequent invoices or estimates.\n\nIt’s the step that fills gaps in your customer list, ensuring seamless data flow. ✍️✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e656e242-fcd7-4f4b-b8c7-1093e6873c9d",
      "name": "Note autocollante7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4160,
        1180
      ],
      "parameters": {
        "width": 500,
        "height": 300,
        "content": "### Step 6: Merge Customer Data Node 🔗📊\n\nThis node merges data from existing customers with newly created customer records.\n\nWhy this step is important:\n\n- 🔄 Combines customer information into a single dataset.\n- 📋 Ensures continuity and completeness of customer details.\n- ⚙️ Prepares unified data for downstream workflow steps.\n\nIt’s the step that brings together all customer data for seamless processing. 🤝✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "43640cbe-d8c0-4d46-a8be-71ed26deaf88",
      "name": "If - ID client Stripe",
      "type": "n8n-nodes-base.if",
      "position": [
        5100,
        1180
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0e0b7c72-589a-4d76-9201-cbc4cfdc9c70",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Search records').item.json['Stripe Customer ID'] }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e7fd405d-2dca-453d-ad16-086d855393e0",
      "name": "Note autocollante8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4840,
        800
      ],
      "parameters": {
        "color": 3,
        "width": 600,
        "height": 340,
        "content": "### Step 7: Stripe Customer ID Check (If Node) 🔍💳\n\nThis node checks whether the **Stripe Customer ID** already exists for users in Airtable.\n\n- **True:** Stripe Customer ID exists; proceeds to find the customer.\n- **False:** Stripe Customer ID does not exist; continues with the workflow to create or update as needed.\n\nWhy this step is important:\n\n- ✅ Prevents duplicate Stripe customer entries.\n- 🔄 Directs the workflow along the correct path based on Stripe data.\n- ⚙️ Maintains data integrity across systems.\n\nIt’s the decision point that manages Stripe customer handling efficiently. 🔀✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d8810872-3110-450d-a87e-a68c6acec976",
      "name": "Note autocollante9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5540,
        680
      ],
      "parameters": {
        "color": 5,
        "width": 580,
        "height": 300,
        "content": "Find Customer in Stripe 🔍💳\n\nThis node uses the **Find Customer** operation to search for a customer in Stripe based on the **Stripe Customer ID**.\n\nWhy this step is important:\n\n- 🔎 Quickly locates the customer record in Stripe.\n- ✅ Ensures accurate synchronization between your systems.\n- 📋 Prevents duplicate customer creation in Stripe.\n\nIt’s the step that verifies Stripe customer existence to maintain clean and consistent billing data. 🧹✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "0af13885-0000-48a4-9a1a-5ca0695d4329",
      "name": "Fusionner - Clients Stripe",
      "type": "n8n-nodes-base.merge",
      "position": [
        6320,
        1460
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "66ce007d-361a-44d1-b397-c8bf354bc62a",
      "name": "Note autocollante10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6180,
        1620
      ],
      "parameters": {
        "color": 6,
        "width": 480,
        "height": 300,
        "content": "### Step 8: Merge Stripe Customer Data Node 🔗💳\n\nThis node merges data from the **If Node** decision with the newly retrieved customer data from the **Stripe - Find Customer** node.\n\nWhy this step is important:\n\n- 🔄 Combines decision results with fresh customer information.\n- 📊 Ensures unified data for further processing in the workflow.\n- ⚙️ Maintains data consistency across Stripe and Airtable.\n\nIt’s the step that integrates conditional logic with actual customer details for smooth automation. 🤝✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "642cf14a-238f-4525-bc6c-a4d88ad2ff25",
      "name": "Note autocollante11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7020,
        780
      ],
      "parameters": {
        "color": 3,
        "width": 540,
        "height": 340,
        "content": "### Step 9: Stripe Customer Existence Check (If Node) 🔍✅❌\n\nThis node checks whether a Stripe customer exists based on the **name**.\n\n- **True:** Customer does **not** exist; proceeds to create a new customer.\n- **False:** Customer exists; continues with the workflow without creating.\n\nWhy this step is important:\n\n- ✅ Prevents duplicate Stripe customer creation.\n- 🔄 Directs workflow to create customers only when necessary.\n- ⚙️ Ensures clean and accurate Stripe customer data.\n\nIt’s the decision gate that manages Stripe customer creation smartly. 🔀✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fe468b60-5f78-4a61-9262-a0c24e2054e8",
      "name": "Note autocollante12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7740,
        640
      ],
      "parameters": {
        "width": 500,
        "height": 320,
        "content": "Create Customer in Stripe ➕💳\n\nThis node uses the **Create Customer** operation to add a new customer in Stripe using the **name** and **email address** from Airtable.\n\nWhy this step is important:\n\n- 🆕 Automatically adds new customers to Stripe when they don’t exist.\n- 📧 Uses accurate customer details from Airtable for Stripe records.\n- 🔗 Ensures Stripe customer data is up-to-date and linked correctly.\n\nIt’s the step that expands your Stripe customer base seamlessly from Airtable data. ✍️✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "84f2c126-13e0-4999-95ed-c62155473934",
      "name": "Note autocollante13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        8320,
        1540
      ],
      "parameters": {
        "color": 5,
        "width": 520,
        "height": 300,
        "content": "### Step 10: Merge New Stripe Customer Data Node 🔗💳\n\nThis node merges data from the previous **If Node** with the newly created Stripe customer data.\n\nWhy this step is important:\n\n- 🔄 Combines decision results with fresh customer creation data.\n- 📊 Provides a unified dataset for downstream processing.\n- ⚙️ Ensures consistency and completeness of customer information.\n\nIt’s the step that integrates newly created Stripe customer details seamlessly into the workflow. 🤝✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d72208de-119a-4cc7-8f41-c04e8534fdf5",
      "name": "Rechercher des enregistrements par e-mail",
      "type": "n8n-nodes-base.airtable",
      "position": [
        9260,
        1080
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_BASE_NAME}"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}/{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_TABLE_NAME}"
        },
        "options": {},
        "operation": "search",
        "filterByFormula": "={Client Email} = \"{{ $json.email }}\""
      },
      "typeVersion": 2.1,
      "alwaysOutputData": true
    },
    {
      "id": "0dc8e7c1-e027-4641-9618-357f72d67039",
      "name": "Note autocollante14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9060,
        760
      ],
      "parameters": {
        "color": 6,
        "width": 480,
        "height": 300,
        "content": "### Step 11: Search Records in Airtable 🔍📋\n\nThis node searches for complete records in the Airtable table based on the **email address** from the previous merge node.\n\nWhy this step is important:\n\n- 🔎 Retrieves full user data linked to the email.\n- 📊 Ensures accurate and updated information for the workflow.\n- ⚙️ Enables precise matching and further processing.\n\nIt’s the step that pulls detailed Airtable records to keep your automation data-rich and reliable. 📚✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "62d46511-8cf8-4160-a242-bea2e4e81086",
      "name": "Mettre à jour les IDs client QuickBooks et Stripe",
      "type": "n8n-nodes-base.airtable",
      "position": [
        9980,
        1400
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_BASE_NAME}"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}/{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_TABLE_NAME}"
        },
        "columns": {
          "value": {
            "id": "={{ $json.id }}",
            "Stripe Customer ID": "={{ $('Merge Stripe Customer').item.json.id }}",
            "QuickBooks Customer ID": "={{ $('Merge QBO Customer').item.json.Id }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Deal Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Deal Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Client Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Email",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Client Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Approved for Invoicing",
                  "value": "Approved for Invoicing"
                },
                {
                  "name": "Draft",
                  "value": "Draft"
                },
                {
                  "name": "Invoiced",
                  "value": "Invoiced"
                }
              ],
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Line Items JSON",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Line Items JSON",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "QuickBooks Customer ID",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "QuickBooks Customer ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stripe Customer ID",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Stripe Customer ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stripe Payment Link",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Stripe Payment Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "QuickBooks Invoice #",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "QuickBooks Invoice #",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update"
      },
      "typeVersion": 2.1
    },
    {
      "id": "5cbaa7bd-1ef6-49d8-8b61-fef3f2d3090c",
      "name": "Note autocollante15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9780,
        1580
      ],
      "parameters": {
        "width": 520,
        "height": 300,
        "content": "### Step 12: Update Records in Airtable ✏️🔄\n\nThis node updates the Airtable records to add or modify the **QuickBooks Customer ID** and **Stripe Customer ID** for the corresponding users.\n\nWhy this step is important:\n\n- 🆕 Keeps Airtable data synchronized with QuickBooks and Stripe.\n- 🔗 Ensures accurate linking of customer IDs across platforms.\n- 🔄 Maintains data integrity for future workflows and reports.\n\nIt’s the step that closes the loop by updating your source data with key customer identifiers. ✅✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "cdd5c85d-1168-4b4e-ab95-b17132f0bd10",
      "name": "Générer des liens de paiement",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10700,
        1080
      ],
      "parameters": {
        "url": "https://api.stripe.com/v1/payment_links",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "line_items[0][price]",
              "value": "={{ $json.fields['Stripe Price Id'] }}"
            },
            {
              "name": "line_items[0][quantity]",
              "value": "={{ $json.fields.Quantity }}"
            }
          ]
        },
        "nodeCredentialType": "stripeApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "86d2ad6f-2501-4885-9f61-40563bcd132a",
      "name": "Note autocollante16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        10500,
        760
      ],
      "parameters": {
        "color": 3,
        "width": 520,
        "height": 300,
        "content": "### Step 13: Generate Stripe Payment Link (HTTP Request) 🔗💳\n\nThis node sends a **POST** HTTP request with the **Stripe Price ID** and **quantity** from Airtable to generate a ready-to-pay link.\n\nWhy this step is important:\n\n- 🛒 Creates a payment link dynamically based on order details.\n- 🔗 Enables easy and secure customer checkout.\n- ⚡ Automates payment initiation directly from your data.\n\nIt’s the step that turns order info into a clickable payment link, simplifying the checkout process. 🔥✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "cce1f3fd-3731-41ea-8b6c-b1b6872f571c",
      "name": "Obtenir tous les produits QuickBooks",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        11440,
        1400
      ],
      "parameters": {
        "url": "https://quickbooks.api.intuit.com/v3/company/{YOUR_QUICKBOOKS_COMPANY_ID}/query?query=SELECT%20*%20FROM%20Item",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "quickBooksOAuth2Api"
      },
      "typeVersion": 4.2
    },
    {
      "id": "e4819fee-6591-45cf-b9e7-000ec6ba07e4",
      "name": "Note autocollante17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        11240,
        1580
      ],
      "parameters": {
        "width": 540,
        "height": 280,
        "content": "### Step 14: Fetch All Products from QuickBooks (HTTP Request) 📦🔍\n\nThis node sends an HTTP request to retrieve all products from QuickBooks.\n\nWhy this step is important:\n\n- 📋 Gathers the full product catalog for reference or processing.\n- 🔄 Keeps your workflow updated with the latest product information.\n- ⚙️ Enables product data integration across your automation.\n\nIt’s the step that pulls your product inventory into the workflow for seamless management. 📦✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "29e7df5d-6b99-44a4-8493-d3c2aec66353",
      "name": "Note autocollante18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12060,
        760
      ],
      "parameters": {
        "color": 6,
        "width": 540,
        "height": 300,
        "content": "### Step 15: Filter Products by Airtable Data (Code Node) ⚙️🔍\n\nThis node runs custom code to filter and return only the product details that exist in Airtable.\n\nWhy this step is important:\n\n- 🎯 Narrows down the product list to relevant items.\n- 🔄 Improves workflow efficiency by processing only necessary data.\n- 📋 Ensures accuracy by matching QuickBooks products with Airtable records.\n\nIt’s the step that refines your product data for targeted automation. 🧹✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b6e2c9d4-502b-44e6-b907-8eab05c7561e",
      "name": "Filtrer et retourner les détails du produit",
      "type": "n8n-nodes-base.code",
      "position": [
        12260,
        1100
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const airtableProductName = $('Update Quickbooks and Stripe Customer Ids').item.json.fields['Quickbooks Product Name'];\nconst quantity = $('Update Quickbooks and Stripe Customer Ids').item.json.fields.Quantity;\n\nconst quickBooksProducts = $json.QueryResponse.Item;\n\nconst matchingProduct = quickBooksProducts.find(product => product.Name === airtableProductName);\n\nif (matchingProduct) {\n  const unitPrice = matchingProduct.UnitPrice || 0;\n  const amount = unitPrice * (quantity || 0);\n\n  return {\n    json: {\n      Id: matchingProduct.Id,\n      Description: matchingProduct.Description,\n      Amount: amount,\n    }\n  };\n} else {\n  return {\n    json: {\n      Id: null,\n      Description: null,\n      Amount: 0,\n    }\n  };\n}\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4e309a57-bebd-4a0e-871f-1f66e7119099",
      "name": "Note autocollante19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12960,
        1620
      ],
      "parameters": {
        "color": 4,
        "width": 480,
        "height": 300,
        "content": "### Step 16: Create Invoice in QuickBooks 🧾✨\n\nThis node uses the **Create Invoice** operation to generate an invoice in QuickBooks based on data from previous nodes.\n\nWhy this step is important:\n\n- 📄 Converts collected data into a formal invoice.\n- 🔗 Links invoices to the correct customers and products.\n- ⚡ Automates billing and record-keeping processes.\n\nIt’s the step that issues the official invoice, completing the sales cycle. ✅✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "52c262a3-df12-47a4-bc8a-73eaaea82ec8",
      "name": "Mettre à jour le lien de paiement Stripe et le numéro de facture QuickBooks",
      "type": "n8n-nodes-base.airtable",
      "position": [
        13840,
        1100
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_BASE_NAME}"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultUrl": "https://airtable.com/{YOUR_AIRTABLE_BASE_ID}/{YOUR_AIRTABLE_TABLE_ID}",
          "cachedResultName": "{YOUR_AIRTABLE_TABLE_NAME}"
        },
        "columns": {
          "value": {
            "id": "={{ $('Update Quickbooks and Stripe Customer Ids').item.json.id }}",
            "Status": "Invoiced",
            "Stripe Payment Link": "={{ $('Generate Payment Links').item.json.url }}",
            "QuickBooks Invoice #": "=INV-{{ $json.DocNumber }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Deal Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Deal Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Client Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Email",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Client Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Approved for Invoicing",
                  "value": "Approved for Invoicing"
                },
                {
                  "name": "Draft",
                  "value": "Draft"
                },
                {
                  "name": "Invoiced",
                  "value": "Invoiced"
                }
              ],
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Line Items JSON",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Line Items JSON",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "QuickBooks Customer ID",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "QuickBooks Customer ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stripe Customer ID",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Stripe Customer ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stripe Payment Link",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Stripe Payment Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "QuickBooks Invoice #",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "QuickBooks Invoice #",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stripe Price Id",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Stripe Price Id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Quantity",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Quantity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Quickbooks Product Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Quickbooks Product Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update"
      },
      "typeVersion": 2.1
    },
    {
      "id": "cba338d9-dbad-4cf8-becc-609892ba96dc",
      "name": "Note autocollante20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13640,
        740
      ],
      "parameters": {
        "color": 5,
        "width": 480,
        "height": 320,
        "content": "### Step 17: Update Airtable Records ✏️🔄\n\nThis node updates Airtable records with the newly created **Stripe Payment Link** and **QuickBooks Invoice #**.\n\nWhy this step is important:\n\n- 🔗 Keeps Airtable synchronized with payment and invoice details.\n- 📋 Provides easy access to transaction information within Airtable.\n- ⚡ Enhances tracking and reporting accuracy.\n\nIt’s the step that ensures your source data reflects the latest billing status. 📈✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e8125d20-5e72-443c-82ca-9f444ff91e1d",
      "name": "Flux de travail terminé",
      "type": "n8n-nodes-base.noOp",
      "position": [
        14660,
        1420
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b3e74b53-f2e4-4c37-adb0-008766c0c21e",
      "name": "Note autocollante21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14500,
        1560
      ],
      "parameters": {
        "color": 4,
        "width": 500,
        "height": 300,
        "content": "### Step 18: Workflow Completion (No-Op Node) ✅🎉\n\nThis **No Operation** node marks the successful completion of the entire workflow.\n\nWhy this step is important:\n\n- 🎯 Signifies a clean and error-free end to the process.\n- 🛡️ Prevents unintended actions after workflow completion.\n- 🔄 Helps maintain clear workflow structure and readability.\n\nIt’s the final step that confirms your automation ran smoothly from start to finish. 🎬✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "75b2998f-2369-425a-b88b-f8e7383861f1",
      "name": "Note autocollante22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        40
      ],
      "parameters": {
        "color": 3,
        "width": 620,
        "height": 680,
        "content": "### Prerequisites ⚙️🔗\n\n#### AIRTABLE\n- Create and connect your Airtable account using a **Personal Access Token**.\n- Create a table with the following columns:\n  - **Deal Name** - Short representation of each record.\n  - **Client Name** - Name of the client/customer.\n  - **Client Email** - Email of the client/customer.\n  - **Status** - Status of the deal (Draft, Approved for Invoicing, Invoiced).\n  - **QuickBooks Customer ID** - ID of the client in QuickBooks.\n  - **Stripe Customer ID** - ID of the client in Stripe.\n  - **Stripe Payment Link** - Payment link from Stripe.\n  - **QuickBooks Invoice #** - Invoice number from QuickBooks.\n  - **Stripe Price Id** - Price ID of the product selected by the client in Stripe.\n  - **Quantity** - Quantity of the product selected by the client.\n  - **QuickBooks Product Name** - Name of the product from QuickBooks.\n  - **Created** - Created timestamp.\n\n- Select the base and table in the **Airtable Trigger** node.\n- Connect and select the same base and table in all required Airtable nodes.\n\n#### QUICKBOOKS\n- Create and connect credentials using **OAuth2 authorization** in QuickBooks.\n- Use the same credentials across all QuickBooks nodes.\n- Add your company ID in the URL in the **Get all Quickbook products** node\n\n#### STRIPE\n- Generate and connect your Stripe account using the **Secret Key**.\n- Use these credentials in all required Stripe nodes.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d88adcf9-9557-4759-9638-bfdd2f6e00b8",
      "name": "Note autocollante23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14500,
        900
      ],
      "parameters": {
        "width": 360,
        "height": 300,
        "content": "### Get in Touch\n\nPlease feel free to reachout to us, if you need any help in settin up this workflow.\n\nWe can also help customize workflow pet the use-case. \n\nReach out us at: getstarted@intuz.com\n\nWebsite: https://www.intuz.com/\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "e10a1c5b-b5c4-4861-a1b7-8cf52907b464",
  "connections": {
    "8b371200-572e-4995-b7d7-d496751342f4": {
      "main": [
        [
          {
            "node": "45863a72-e832-448e-88e6-edb6ee1c8908",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "989dcbdc-5d15-45df-bb0e-db6049c537e2": {
      "main": [
        [
          {
            "node": "8b371200-572e-4995-b7d7-d496751342f4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b1330d78-6e36-4296-98b0-0312804e955d": {
      "main": [
        [
          {
            "node": "2cb49f5e-08f7-46ea-afab-217e98908a14",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "497c8d82-1916-4ac6-acd2-7fdc561d3919": {
      "main": [
        [
          {
            "node": "52c262a3-df12-47a4-bc8a-73eaaea82ec8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "45863a72-e832-448e-88e6-edb6ee1c8908": {
      "main": [
        [
          {
            "node": "4c99441c-1f72-49d6-b421-62e3c2c72b28",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "12d6c762-e245-4203-b91d-a03ad0c39c5a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2cb49f5e-08f7-46ea-afab-217e98908a14": {
      "main": [
        [
          {
            "node": "43640cbe-d8c0-4d46-a8be-71ed26deaf88",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "43c615f1-777f-43da-8274-2b9ecce18aa0": {
      "main": [
        [
          {
            "node": "b1330d78-6e36-4296-98b0-0312804e955d",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "2cb49f5e-08f7-46ea-afab-217e98908a14",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "901e1828-f5e5-4801-83a0-53fce11f4ea0": {
      "main": [
        [
          {
            "node": "d72208de-119a-4cc7-8f41-c04e8534fdf5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "cdd5c85d-1168-4b4e-ab95-b17132f0bd10": {
      "main": [
        [
          {
            "node": "cce1f3fd-3731-41ea-8b6c-b1b6872f571c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "60920472-77b6-4243-89b0-f162f6e900d9": {
      "main": [
        [
          {
            "node": "0af13885-0000-48a4-9a1a-5ca0695d4329",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "43640cbe-d8c0-4d46-a8be-71ed26deaf88": {
      "main": [
        [
          {
            "node": "60920472-77b6-4243-89b0-f162f6e900d9",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "0af13885-0000-48a4-9a1a-5ca0695d4329",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d72208de-119a-4cc7-8f41-c04e8534fdf5": {
      "main": [
        [
          {
            "node": "62d46511-8cf8-4160-a242-bea2e4e81086",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0af13885-0000-48a4-9a1a-5ca0695d4329": {
      "main": [
        [
          {
            "node": "bd0adf89-499a-40ee-9e37-648ae0aad30d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ea27b7d1-4e6e-4620-b402-b0b8e0db436d": {
      "main": [
        [
          {
            "node": "901e1828-f5e5-4801-83a0-53fce11f4ea0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "cce1f3fd-3731-41ea-8b6c-b1b6872f571c": {
      "main": [
        [
          {
            "node": "b6e2c9d4-502b-44e6-b907-8eab05c7561e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4c99441c-1f72-49d6-b421-62e3c2c72b28": {
      "main": [
        [
          {
            "node": "43c615f1-777f-43da-8274-2b9ecce18aa0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "bd0adf89-499a-40ee-9e37-648ae0aad30d": {
      "main": [
        [
          {
            "node": "ea27b7d1-4e6e-4620-b402-b0b8e0db436d",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "901e1828-f5e5-4801-83a0-53fce11f4ea0",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "b6e2c9d4-502b-44e6-b907-8eab05c7561e": {
      "main": [
        [
          {
            "node": "497c8d82-1916-4ac6-acd2-7fdc561d3919",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "62d46511-8cf8-4160-a242-bea2e4e81086": {
      "main": [
        [
          {
            "node": "cdd5c85d-1168-4b4e-ab95-b17132f0bd10",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "52c262a3-df12-47a4-bc8a-73eaaea82ec8": {
      "main": [
        [
          {
            "node": "e8125d20-5e72-443c-82ca-9f444ff91e1d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Avancé - Traitement des factures, IA Multimodale

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Informations sur le workflow
Niveau de difficulté
Avancé
Nombre de nœuds46
Catégorie2
Types de nœuds10
Description de la difficulté

Adapté aux utilisateurs avancés, avec des workflows complexes contenant 16+ nœuds

Auteur
Intuz

Intuz

@intuz

Workflow automation can help automate your routine activities and help saves $$$, as well as hours of time. As a boutique tech consulting company, Intuz help businesses with custom AI/ML, AI Workflow Automations, and software development. Automate your business workflow for: Sales Marketing Accounting Finance Operations E-Commerce Customer Support Admin & Backoffice Logistics & Supply Chain

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34