The shortcut, and where my notes went wrong
The dashboard button is convenient: no redirect URI, no browser flow. My notes from the previous account said it returns a long-lived token directly and that the separate exchange step was optional. On the next setup the button returned a short-lived token. Whether Meta changed the behaviour or my earlier check was too shallow, I can't say. What I can say is that a token that looks fine is indistinguishable from a good one until you ask Meta about it.
Ask the token itself
Threads has a debug endpoint that takes the token as both the input and the credential:
GET https://graph.threads.net/debug_token?input_token=<TOKEN>&access_token=<TOKEN>
The response includes the scopes, the user id and expires_at as a Unix timestamp. Converting that timestamp is the whole test. Mine was about an hour and a half after generation.
The exchange
Meta's Threads documentation describes an exchange call that trades a short-lived user token for a long-lived one, using the th_exchange_token grant type together with the app's secret. Run it from the server, never from a browser or from a workflow that logs its inputs:
GET https://graph.threads.net/access_token?grant_type=th_exchange_token&client_secret=<APP_SECRET>&access_token=<SHORT_LIVED_TOKEN>
The response carries the long-lived token, and debug_token on it showed an expiry about 60 days out. That token went into an n8n HTTP query-auth credential and the short-lived one was discarded.
The Page token had the opposite surprise
For the Facebook Page side I assumed the token from the Page-accounts lookup was also a 60-day token. debug_token said expires_at: 0, which means it doesn't expire. Assuming 60 days would have put a pointless refresh job on the calendar. Assuming "forever" without checking would have been the more expensive mistake in the other direction.
What I do now
- Run debug_token on every token before it goes into a credential, and write the expiry date next to it.
- Schedule the refresh of a long-lived Threads token before it lapses (Meta documents a refresh call), rather than finding out on day 60 from a failed post.
- Keep the app secret out of any node that logs its parameters.
A different Threads failure, unrelated to tokens, is covered in a 500-character limit hiding behind a generic error.
Short-lived, long-lived, refreshed: the three Threads tokens
| Token | Where it comes from | Lifetime (as checked with debug_token) |
|---|---|---|
| Short-lived user token | OAuth flow, or the dashboard's Generate Token button in my case | About 1 to 2 hours |
| Long-lived user token | th_exchange_token exchange with the app secret | About 60 days |
| Refreshed long-lived token | th_refresh_token call on a long-lived token | A new 60-day window |
The only reliable way to know which one you are holding is to ask the debug endpoint. The length or format of the token string tells you nothing.
Keeping the token alive inside n8n
Meta documents a refresh call for long-lived Threads tokens:
GET https://graph.threads.net/refresh_access_token?grant_type=th_refresh_token&access_token=<LONG_LIVED_TOKEN>
According to Meta's documentation the token has to be at least 24 hours old and still valid to be refreshed, so an expired token cannot be rescued this way. That makes the schedule the important part. What I use:
- Write the expiry date into a note or a sheet the moment the long-lived token is stored.
- Run a small scheduled workflow every few weeks that calls the refresh endpoint and writes the new token into the HTTP credential.
- If the refresh call fails, send a Telegram alert. A failed refresh at day 40 leaves almost three weeks to reconnect by hand. A failed post at day 60 leaves none.
Updating a credential from a workflow means calling the n8n API, and that call carries the token in its body. Keep that workflow's execution data saving off for successful runs, or the token ends up readable in the execution history.
A five-minute checklist for a new Threads account
- The account is added as a Threads tester in the Meta app, and the invite was accepted from the account's own settings.
- The token in hand was checked with
debug_token, and the expiry was converted from the Unix timestamp. - If it expires within hours, it was exchanged with
th_exchange_tokenon the server, and the long-lived token was checked again. - The app secret never went into a node whose parameters are saved in execution data.
- A refresh or reminder is on the calendar before day 60.
Frequently asked questions
How long does a Threads access token last?
A short-lived token lasts about an hour or two, and a long-lived token about 60 days. Check the actual expiry of your token with the debug_token endpoint rather than relying on where it came from.
Can I refresh an expired Threads token?
No. Meta's refresh call only works on a long-lived token that is still valid. Once it has expired you have to go through the authorization again.
Where should the app secret live?
Only in the server-side call that does the exchange, or in an n8n credential. Never in a node parameter, because node inputs can be stored in execution data.
Does the Facebook Page token expire too?
The Page token in this setup reported expires_at 0 in debug_token, which means it does not expire. Check your own token, because the result depends on how it was obtained.
n8n Automation Hub