{
  "id": "HnVFowdm6-T8Itn60-5Zl",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Track OpenAI token usage to Google Sheets with monthly email reports",
  "tags": [],
  "nodes": [
    {
      "id": "78fca6be-db2a-4b21-88ca-3d64e6881b32",
      "name": "Fetch OpenAI Usage Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        32,
        144
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/organization/usage/completions",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "start_time",
              "value": "={{ Math.floor(Date.UTC(new Date().getFullYear(), new Date().getMonth() - 1, 1) / 1000) }}"
            },
            {
              "name": "end_time",
              "value": "={{ Math.floor(Date.UTC(new Date().getFullYear(), new Date().getMonth(), 0, 23, 59, 59) / 1000) }}"
            },
            {
              "name": "api_key_ids[]",
              "value": "your-api-key-id"
            },
            {
              "name": "limit",
              "value": "31"
            },
            {
              "name": "bucket_width",
              "value": "1d"
            },
            {
              "name": "group_by[]",
              "value": "model"
            }
          ]
        },
        "nodeCredentialType": "openAiApi"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "2d0bd1b3-bba6-4bf0-8b3a-a52b2382e664",
      "name": "Transform Usage to Daily Breakdown",
      "type": "n8n-nodes-base.code",
      "position": [
        240,
        144
      ],
      "parameters": {
        "jsCode": "// Get the data from the HTTP Request node\nconst inputData = $input.all();\n\n// The data is directly an array with one object that has a 'data' property\nconst rawData = inputData[0].json.data;\n\n// Transform each bucket (day) into a flat structure\nconst outputData = [];\n\nfor (const bucket of rawData) {\n  const date = bucket.start_time_iso.split('T')[0]; // Get just YYYY-MM-DD\n  \n  // If there are results for this day\n  if (bucket.results && bucket.results.length > 0) {\n    for (const result of bucket.results) {\n      outputData.push({\n        date: date,\n        model: result.model || 'N/A',\n        input_tokens: result.input_tokens || 0,\n        output_tokens: result.output_tokens || 0\n      });\n    }\n  } else {\n    // If no usage for this day, add a row showing zero usage\n    outputData.push({\n      date: date,\n      model: 'No usage',\n      input_tokens: 0,\n      output_tokens: 0\n    });\n  }\n}\n\nreturn outputData.map(item => ({ json: item }));"
      },
      "typeVersion": 2
    },
    {
      "id": "96775d45-7c96-4888-960c-14058f627f17",
      "name": "Append Data to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        144
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json.date }}",
            "Model": "={{ $json.model }}",
            "Token Usage In": "={{ $json.input_tokens }}",
            "Token Usage Out": "={{ $json.output_tokens }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Model",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Model",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Token Usage In",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Token Usage In",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Token Usage Out",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Token Usage Out",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Create Monthly Report from Template').item.json.id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "77773788-5d6a-4a91-bd46-601d4fdbc6ff",
      "name": "Create Monthly Report from Template",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -240,
        144
      ],
      "parameters": {
        "name": "=Token_Tracking_{{ $now.minus({months: 1}).toFormat('LLLL') }}_{{ $now.minus({months: 1}).toFormat('yyyy') }}",
        "fileId": {
          "__rl": true,
          "mode": "list",
          "value": "your-template-file-id"
        },
        "options": {},
        "operation": "copy"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "4b6b6fea-51b8-47c1-95b5-51cc7716b5d9",
      "name": "Export Sheet as Excel",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        704,
        64
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Create Monthly Report from Template').item.json.id }}"
        },
        "options": {
          "fileName": "={{ $('Create Monthly Report from Template').item.json.name }}.xlsx",
          "binaryPropertyName": "data",
          "googleFileConversion": {
            "conversion": {
              "sheetsToFormat": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            }
          }
        },
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 3
    },
    {
      "id": "5d0e6993-e7ed-4a5a-a344-0fded932b4ec",
      "name": "Archive Report to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        944,
        64
      ],
      "parameters": {
        "name": "={{ $('Create Monthly Report from Template').item.json.name }}.xlsx",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "your-archive-folder-id"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 3
    },
    {
      "id": "c739b7b5-c19f-405c-8862-44012dbaa49a",
      "name": "Email Report to Stakeholder",
      "type": "n8n-nodes-base.gmail",
      "position": [
        944,
        208
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "Please see attached token usage data for the previous month.",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {}
            ]
          },
          "appendAttribution": false
        },
        "subject": "Monthly OpenAI Token Usage Report"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "d886ba78-d654-4ecc-9bb9-5b4f22e36ac0",
      "name": "Export Sheet as PDF for Email",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        704,
        208
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Create Monthly Report from Template').item.json.id }}"
        },
        "options": {
          "fileName": "={{ $('Create Monthly Report from Template').item.json.name }}.pdf",
          "binaryPropertyName": "data",
          "googleFileConversion": {
            "conversion": {
              "sheetsToFormat": "application/pdf"
            }
          }
        },
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 3
    },
    {
      "id": "ac6f42bc-d87b-4a45-a23a-4703d170154e",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        -400
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 1316,
        "content": "## Track OpenAI Token Usage with Automated Monthly Reports\n\nThis workflow automatically monitors your OpenAI API usage and costs on a monthly basis. Perfect for finance teams and developers who need to track AI spending across different models and projects.\n\n### How it works\n* Runs automatically on the 5th of each month via Schedule Trigger\n* Creates a new Google Sheet from your template for the reporting period (template can be found [HERE](https://github.com/WorkFlowAutomationNetwork/Youtube-Resources/tree/main/N8NTemplate) )\n* Fetches previous month's usage data from OpenAI API\n* Transforms raw API data into daily breakdown by model\n* Appends usage data to Google Sheets (input/output tokens per model)\n* Your sheet calculates costs automatically using formulas\n* Generates PDF report and Excel file\n* Emails the PDF report to stakeholders\n* Archives Excel file in Google Drive for recordkeeping\n\n### Requirements\n* OpenAI Admin API access (to access organization usage endpoints)\n* Google Sheets template with cost calculation formulas\n* Google Drive for storage\n* Gmail account for email notifications\n\n### Setup Instructions\n1. **Create Google Sheets template** with columns: Date, Model, Token Usage In, Token Usage Out, Token Cost Input, Token Cost Output, Total Cost USD (add formulas for automatic calculations) (note a template is provided if you don't want to create your own)\n2. **Configure credentials** in n8n for OpenAI API, Google Sheets, Google Drive, and Gmail\n3. **Update the HTTP Request node** with your OpenAI API key ID\n4. **Replace placeholder email** (your-email@example.com) with recipient address\n5. **Set your Google Drive folders** for template source and archive destination\n6. **Test the workflow** using the manual trigger before enabling schedule\n\n### Customization Ideas\n* Track multiple API keys by adding more `api_key_ids[]` parameters\n* Change reporting frequency by modifying the Schedule Trigger\n* Add Slack notifications instead of or in addition to email\n* Include budget threshold alerts using IF nodes\n* Add cost forecasting based on trends\n* Track additional metrics like average tokens per request\n* Support multiple currencies with conversion rates\n"
      },
      "typeVersion": 1
    },
    {
      "id": "838ae489-19f4-480c-a6a8-a9962cdf752d",
      "name": "Monthly Report Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -528,
        144
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtDayOfMonth": 5
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c75ec0dc-4d1b-481a-b8e9-28e2d07d225e",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 544,
        "content": "## Step 2: Fetch and Process Usage Data\n\nRetrieves OpenAI usage data for the previous month via API, transforms it into a daily breakdown by model (input/output tokens), and populates the Google Sheet.\n\n**Note:** Make sure to replace 'your-api-key-id' with your actual OpenAI API key ID in the HTTP Request node."
      },
      "typeVersion": 1
    },
    {
      "id": "9bb45d33-875c-4286-9df8-6830cf04288e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 544,
        "content": "## Step 3: Generate and Distribute Report\n\nDownloads the completed sheet as both PDF and Excel formats, emails the PDF to stakeholders with usage summary, and archives the Excel file in Google Drive for long-term storage.\n\n**Remember to update:** Email recipient address and Google Drive archive folder ID."
      },
      "typeVersion": 1
    },
    {
      "id": "b3e56d33-58f6-479d-b00a-9722601489c0",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -576,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 544,
        "content": "## Step 1: Initialize Monthly Report\n\nTriggers on the 5th of each month and copies the Google Sheets template with proper naming convention (e.g., 'Token_Tracking_December_2025').\n\n**Update:** Set your template file ID in the Google Drive node."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "54d77165-1ab3-4a88-a799-60ff1032a998",
  "connections": {
    "Export Sheet as Excel": {
      "main": [
        [
          {
            "node": "Archive Report to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly Report Trigger": {
      "main": [
        [
          {
            "node": "Create Monthly Report from Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch OpenAI Usage Data": {
      "main": [
        [
          {
            "node": "Transform Usage to Daily Breakdown",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Data to Google Sheet": {
      "main": [
        [
          {
            "node": "Export Sheet as Excel",
            "type": "main",
            "index": 0
          },
          {
            "node": "Export Sheet as PDF for Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Export Sheet as PDF for Email": {
      "main": [
        [
          {
            "node": "Email Report to Stakeholder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform Usage to Daily Breakdown": {
      "main": [
        [
          {
            "node": "Append Data to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Monthly Report from Template": {
      "main": [
        [
          {
            "node": "Fetch OpenAI Usage Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}