New to Kestra?
Use blueprints to kickstart your first workflows.
Rotate LinkedIn OAuth2 access tokens with Kestra. Exchange a refresh token monthly, get expiry and scope outputs, and confirm each rotation in Slack.
LinkedIn access tokens expire, and every automation built on one stops when it does. This blueprint runs io.kestra.plugin.linkedin.OAuth2 on a monthly schedule to exchange a long-lived refresh token for a fresh access token. The task returns accessToken, tokenType, expiresIn, expiresAt, and scope as outputs, and the flow confirms each rotation in Slack using only the non-sensitive scalars.
refresh_token (io.kestra.plugin.linkedin.OAuth2) posts the client ID, client secret, and refresh token to LinkedIn's token endpoint and returns the new credentials as task outputs.{{ outputs.refresh_token.accessToken }}, no storage step required.confirm posts the token type, granted scope, and expiry to Slack. The token value itself is deliberately kept out of the message.errors block sends a distinct Slack alert when the exchange fails, the usual cause is a revoked or expired refresh token.Schedule trigger rotates the token on the first of every month.expiresIn and expiresAt outputs so the team always knows the token's remaining lifetime.{{ outputs.refresh_token.accessToken }} in the same flow.Token rotation is exactly the kind of small, critical chore that gets forgotten. Kestra gives it a schedule, keeps the client secret and refresh token in namespace secrets, records every rotation in the execution history, and raises a Slack alert the one time it fails.
LINKEDIN_CLIENT_ID: OAuth2 client ID from the LinkedIn Developer Portal.LINKEDIN_CLIENT_SECRET: OAuth2 client secret from the LinkedIn Developer Portal.LINKEDIN_REFRESH_TOKEN: refresh token from the initial authorization flow.SLACK_WEBHOOK_URL: Slack incoming webhook URL.disabled: false on the monthly trigger.io.kestra.plugin.core.kv.Set after the exchange, then read it elsewhere with the kv() function.LINKEDIN_ACCESS_TOKEN secret itself through the secrets API so every existing flow picks up the new value transparently.io.kestra.plugin.linkedin.GetPostAnalytics directly after the exchange with accessToken: "{{ outputs.refresh_token.accessToken }}" so analytics always run on a token minted seconds earlier.expiresIn against a threshold in a follow-up task.