This workflow corresponds to n8n.io template #13452 — we link there as the canonical source.
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 →
{
"name": "Glasp Highlights Auto Export",
"tags": [
{
"name": "Glasp"
},
{
"name": "Highlights"
},
{
"name": "Export"
},
{
"name": "Automation"
}
],
"nodes": [
{
"id": "schedule-trigger",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
220,
300
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"typeVersion": 1.2
},
{
"id": "prepare-params",
"name": "Prepare Parameters",
"type": "n8n-nodes-base.code",
"position": [
460,
300
],
"parameters": {
"jsCode": "// Prepare the query parameters for the Glasp API\nconst sd = $getWorkflowStaticData('global');\nconst now = new Date();\nconst firstRun = !sd.lastRunAt;\n\nlet baseDate = firstRun\n ? new Date(now.getTime() - 24 * 60 * 60 * 1000)\n : new Date(sd.lastRunAt);\n\n// 5-minute buffer\nbaseDate = new Date(baseDate.getTime() - 5 * 60 * 1000);\n\nsd.exportedDocs = sd.exportedDocs || {};\n\nreturn [\n {\n json: {\n updatedAfter: baseDate.toISOString(),\n exportedDocs: sd.exportedDocs,\n },\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "http-glasp-api",
"name": "Glasp API",
"type": "n8n-nodes-base.httpRequest",
"position": [
700,
300
],
"parameters": {
"url": "https://api.glasp.co/v1/highlights/export",
"method": "GET",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
},
"pagination": {
"nextURL": "={{ $response.body.nextPageCursor ? 'https://api.glasp.co/v1/highlights/export?updatedAfter=' + $json.updatedAfter + '&pageCursor=' + $response.body.nextPageCursor : '' }}",
"maxRequests": 100,
"paginationMode": "responseContainsNextURL",
"paginationCompleteWhen": "receiveSpecificStatusCodes",
"statusCodesWhenComplete": ""
}
},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "updatedAfter",
"value": "={{ $json.updatedAfter }}"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "filter-and-format",
"name": "Filter & Format",
"type": "n8n-nodes-base.code",
"position": [
940,
300
],
"parameters": {
"jsCode": "// Filter out already-exported docs, then format for export\nconst sd = $getWorkflowStaticData('global');\nsd.exportedDocs = sd.exportedDocs || {};\nconst now = new Date();\n\n// Collect all results from all paginated responses\nconst allItems = $input.all();\nlet allResults = [];\n\nfor (const item of allItems) {\n const data = item.json;\n // Handle both: direct results array or nested in response\n if (data.results && Array.isArray(data.results)) {\n allResults = allResults.concat(data.results);\n }\n}\n\n// Filter: only new docs with highlights\nconst newDocs = allResults.filter(function(doc) {\n return !sd.exportedDocs[doc.id] && doc.highlights && doc.highlights.length > 0;\n});\n\n// Mark as exported\nfor (const doc of newDocs) {\n sd.exportedDocs[doc.id] = now.toISOString();\n}\n\n// Update last run\nsd.lastRunAt = now.toISOString();\n\n// Cleanup old tracking (30 days)\nconst thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\nfor (const [docId, exportedAt] of Object.entries(sd.exportedDocs)) {\n if (new Date(exportedAt) < thirtyDaysAgo) {\n delete sd.exportedDocs[docId];\n }\n}\n\nif (newDocs.length === 0) {\n return [{ json: { message: 'No new highlights found.', count: 0 } }];\n}\n\n// Format each doc for export\nreturn newDocs.map(function(doc) {\n var lines = [];\n lines.push('## ' + (doc.title || 'Untitled'));\n lines.push('');\n lines.push('**URL:** ' + doc.url);\n lines.push('**Glasp:** ' + doc.glasp_url);\n if (doc.tags && doc.tags.length > 0) {\n lines.push('**Tags:** ' + doc.tags.join(', '));\n }\n if (doc.document_note) {\n lines.push('**Note:** ' + doc.document_note);\n }\n lines.push('');\n lines.push('### Highlights');\n lines.push('');\n\n for (var i = 0; i < doc.highlights.length; i++) {\n var h = doc.highlights[i];\n lines.push('> ' + h.text);\n if (h.note) {\n lines.push('');\n lines.push('**Note:** ' + h.note);\n }\n var d = new Date(h.highlighted_at).toLocaleDateString();\n lines.push('');\n lines.push('- *' + h.color + ' highlight, ' + d + '*');\n lines.push('');\n }\n\n var highlightsMarkdown = lines.join('\\n');\n\n var highlightsText = doc.highlights\n .map(function(h) {\n return h.text + (h.note ? ' [Note: ' + h.note + ']' : '');\n })\n .join('\\n\\n');\n\n return {\n json: {\n documentId: doc.id,\n title: doc.title || 'Untitled',\n url: doc.url,\n glasp_url: doc.glasp_url,\n domain: doc.domain,\n category: doc.category,\n tags: doc.tags || [],\n author: doc.author || '',\n thumbnail_url: doc.thumbnail_url || '',\n document_note: doc.document_note || '',\n is_favorite: doc.is_favorite || false,\n highlightCount: doc.highlights.length,\n highlightsText: highlightsText,\n highlightsMarkdown: highlightsMarkdown,\n highlights: doc.highlights.map(function(h) {\n return {\n id: h.id,\n text: h.text,\n note: h.note || '',\n color: h.color,\n highlighted_at: h.highlighted_at,\n };\n }),\n createdAt: doc.created_at,\n updatedAt: doc.updated_at,\n },\n };\n});\n"
},
"typeVersion": 2
},
{
"id": "sticky-note-destination",
"name": "Sticky Note - Destination Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
1200,
100
],
"parameters": {
"width": 360,
"height": 440,
"content": "## [Connect Your Destination Here]\n\nAdd your preferred export node after \"Filter & Format\".\n\n**Available fields per item:**\n- `title` -- Article/page title\n- `url` -- Original URL\n- `glasp_url` -- Glasp page URL\n- `domain`, `category`, `tags`\n- `highlightCount`\n- `highlightsText` -- Plain text\n- `highlightsMarkdown` -- Markdown\n- `highlights[]` -- Array of objects\n- `createdAt`, `updatedAt`\n\n**Examples:**\n- Notion: Create Database Item\n- Slack: Send Message\n- Google Sheets: Append Row\n- Email: Send Email\n- Webhook: HTTP Request POST"
},
"typeVersion": 1
},
{
"id": "sticky-note-setup",
"name": "Sticky Note - Setup",
"type": "n8n-nodes-base.stickyNote",
"position": [
180,
-100
],
"parameters": {
"width": 360,
"height": 320,
"content": "## [Setup - 2 steps]\n\n**Step 1:** Get your Glasp Access Token\nhttps://glasp.co/settings/access_token\n\n**Step 2:** Create a \"Header Auth\" credential\n- Name: Authorization\n- Value: Bearer YOUR_TOKEN\nThen assign it to the \"Glasp API\" node.\n\n**Optional:** Adjust schedule frequency\nby clicking the Schedule Trigger node.\nDefault: every 6 hours."
},
"typeVersion": 1
},
{
"id": "sticky-note-security",
"name": "Sticky Note - Security",
"type": "n8n-nodes-base.stickyNote",
"position": [
600,
-100
],
"parameters": {
"width": 360,
"height": 220,
"content": "## [Security]\n\n- Access token is stored in n8n's\n encrypted Credentials, never in\n the workflow JSON.\n- Exported doc tracking auto-cleans\n after 30 days.\n- No secrets in code."
},
"typeVersion": 1
}
],
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"connections": {
"Glasp API": {
"main": [
[
{
"node": "Filter & Format",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Prepare Parameters",
"type": "main",
"index": 0
}
]
]
},
"Prepare Parameters": {
"main": [
[
{
"node": "Glasp API",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Runs on a schedule (default: every 6 hours) and fetches new Glasp highlights via the Glasp API Filters out already-exported documents to avoid duplicates Formats highlights as plain text and Markdown Sends them to any destination you connect (Notion, Slack, Google Sheets,…
Source: https://n8n.io/workflows/13452/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Auto Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.
Auto-Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.
debug. Uses httpRequest, slack, redis, mailgun. Scheduled trigger; 60 nodes.
Seller Follow-Up Engine (Enhanced). Uses httpRequest, slack. Scheduled trigger; 44 nodes.
This workflow runs daily at 9 AM, uses Perplexity to compile vaping industry news, optionally captures article screenshots via Browserless, and generates a HeyGen avatar video from a template. It then