{ "name": "自動出 Post · Threads Token Refresh", "nodes": [ { "id": "n8n-id-001", "name": "Every 30 Days", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ 0, 0 ], "parameters": { "rule": { "interval": [ { "field": "days", "daysInterval": 30 } ] } } }, { "id": "n8n-id-002", "name": "Read Stored Token", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 224, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const s = $getWorkflowStaticData('global');\nreturn [{ json: { token: s.fb_token || '' } }];" } }, { "id": "n8n-id-003", "name": "Exchange for Long-Lived", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 448, 0 ], "parameters": { "method": "GET", "url": "https://graph.facebook.com/v21.0/oauth/access_token", "sendQuery": true, "specifyQuery": "keypair", "queryParameters": { "parameters": [ { "name": "grant_type", "value": "fb_exchange_token" }, { "name": "client_id", "value": "YOUR_META_APP_ID" }, { "name": "client_secret", "value": "YOUR_META_APP_SECRET" }, { "name": "fb_exchange_token", "value": "={{ $json.token }}" } ] } } }, { "id": "n8n-id-004", "name": "Save New Token", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 672, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const s = $getWorkflowStaticData('global');\ns.fb_token = $json.access_token;\nreturn [{ json: { status: 'Token saved to static data', token: s.fb_token } }];" } }, { "id": "n8n-id-005", "name": "Read Threads Token", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 224, 200 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const s = $getWorkflowStaticData('global');\nreturn [{ json: { token: s.th_token || '' } }];" } }, { "id": "n8n-id-006", "name": "Refresh Threads Token", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 448, 200 ], "parameters": { "method": "GET", "url": "https://graph.threads.net/refresh_access_token", "sendQuery": true, "specifyQuery": "keypair", "queryParameters": { "parameters": [ { "name": "grant_type", "value": "th_refresh_token" }, { "name": "access_token", "value": "={{ $json.token }}" } ] }, "options": {} } }, { "id": "n8n-id-007", "name": "Save Threads Token", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 672, 200 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const s = $getWorkflowStaticData('global');\nif ($json.access_token) { s.th_token = $json.access_token; }\nreturn [{ json: { status: $json.access_token ? 'Threads token refreshed (60d)' : 'Threads refresh FAILED (no access_token in response)', token: s.th_token || '' } }];" } }, { "id": "n8n-id-008", "name": "Note - Token Refresh", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -40, -340 ], "parameters": { "content": "## Token refresh (2 parallel chains, every 30 days)\nTop chain = Meta/FB long-lived token (grant_type=fb_exchange_token, needs client_id+secret).\nBottom chain = Threads token: GET graph.threads.net/refresh_access_token?grant_type=th_refresh_token&access_token=. No client_secret. Refreshed token lasts 60 days; token must be >=24h old and not yet expired.\n\nSETUP:\n1. Seed initial tokens into static data once (run a one-off Code node: s.fb_token=..., s.th_token=...), or paste after first manual token grab.\n2. Replace YOUR_META_APP_ID / YOUR_META_APP_SECRET in the FB exchange node.\n\nKNOWN LIMIT: n8n cannot auto-update a saved CREDENTIAL from a workflow. The posting workflow reads tokens from its credentials, not from this static data. So either (a) every ~50 days copy the refreshed token from here into the credential, or (b) switch the posting HTTP nodes to read the token from static data via expression. For the example setup the FB USER token is non-expiring, so only Threads (60d) actually needs this.", "height": 320, "width": 520, "color": 5 } } ], "connections": { "Every 30 Days": { "main": [ [ { "node": "Read Stored Token", "type": "main", "index": 0 }, { "node": "Read Threads Token", "type": "main", "index": 0 } ] ] }, "Read Stored Token": { "main": [ [ { "node": "Exchange for Long-Lived", "type": "main", "index": 0 } ] ] }, "Exchange for Long-Lived": { "main": [ [ { "node": "Save New Token", "type": "main", "index": 0 } ] ] }, "Read Threads Token": { "main": [ [ { "node": "Refresh Threads Token", "type": "main", "index": 0 } ] ] }, "Refresh Threads Token": { "main": [ [ { "node": "Save Threads Token", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" }, "active": false }