web
You’re offline. This is a read only version of the page.
close
Skip to main content

Announcements

No record found.

News and Announcements icon
Community site session details

Community site session details

Session Id :
Small and medium business | Business Central, N...
Answered

AL dev - how to check bearer token expiry via AL code

(8) ShareShare
ReportReport
Posted on by 3,297
Hi All
We are calling external API from BC where Bearer Token with expiry. I couldn't find an example how best to check if the token is expired. One way is to populate a datetime field with token expiry datetime (or token create time ) and check against this when call authentication API
I have the same question (0)
  • Suggested answer
    Rishabh Kanaskar Profile Picture
    6,219 Super User 2026 Season 1 on at
    Hi,
     
    Yes, the recommended way is to store the token expiry datetime in a field when you receive the token. Before each API call, compare CurrentDateTime with the stored expiry:
     
    if CurrentDateTime >= TokenExpiry then
        RefreshToken();

    There’s no built-in AL function to check token validity; you must manage it via stored expiry.
     
    Thanks
    Rishabh
  • Suggested answer
    Nimsara Jayathilaka. Profile Picture
    4,950 Super User 2026 Season 1 on at
    Hi
     
    Yes — your idea is correct. The best practice is to store the token expiry DateTime and always check against CURRENTDATETIME before calling the API. If expired (or about to expire), refresh/re-authenticate first. Wrap this into a GetAccessToken() function so your API calls are always safe.
     
    Thanks
    Nimsara
  • Suggested answer
    Sahan Hasitha Profile Picture
    2,683 on at
    hi
    When calling an external API from Business Central that uses a Bearer token with expiry, the best practice is to store both the token and its calculated expiry time in a table. Each time you need to call the API, check whether the stored expiry time is still valid (adding a small safety buffer). If the token has expired or is missing, request a new token from the authentication endpoint, update the stored values, and then proceed with the API call. This approach avoids unnecessary authentication requests while ensuring that the API calls always use a valid token.
  • Suggested answer
    Sumit Singh Profile Picture
    11,757 Super User 2026 Season 1 on at
    Hi,
    You can try below methods to handle expiring Bearer tokens when calling external APIs from Business Central.

    The pattern (proactive + reactive)

    1. Cache token + expiry, refresh early
      • When you obtain a token, also read expires_in and compute ExpiresAt := Now() + expires_in - SafetyBuffer (e.g., minus 2–5 minutes).
      • Before each call, if Now() >= ExpiresAt → refresh; else reuse the token.
      • Use BC’s HttpClient for the call and set Authorization: Bearer <token>. [1]
    2. Retry once on 401/invalid token (reactive)
      • If the API responds 401/403, assume the token is expired/invalid, refresh once and retry the request; if it fails again, stop and surface the error. Microsoft’s guidance lists 4xx handling (incl. 401) and recommends fixing/renewing auth in your calling code. [2]
    3. Use BC’s built‑in OAuth2 codeunit
      • Acquire/refresh tokens with Codeunit OAuth2 methods (Authorization Code, Client Credentials, etc.). Newer signatures use SecretText for the access token. Store only the token and computed expiry you need for subsequent calls. [3]

    Official docs (Microsoft)

    References
    [1] Call external services with the HttpClient data type - Business Central ...
    [2] Troubleshooting web service errors (OData, API, and SOAP) - Business ...
    [3] Codeunit OAuth2 | Microsoft Learn
    [4] Using OAuth to authenticate Business Central Web Services - Business ...
    Response created with the help of Co-Pilot. Please mark “Verified” if it helps.
  • Suggested answer
    YUN ZHU Profile Picture
    99,055 Super User 2026 Season 1 on at
    If the API is not called very frequently, it is not a bad idea to refresh the token every time.
     
    Thanks.
    ZHU
     
  • Suggested answer
    Jeffrey Bulanadi Profile Picture
    9,112 Super User 2026 Season 1 on at

    Hi Samantha,

    When integrating external APIs with bearer tokens in BC, tracking token expiry is essential to avoid failed calls and unnecessary re-authentication. Since AL doesn’t natively decode JWT tokens or expose expiry metadata, the cleanest approach is to store and compare timestamps manually.

    Here’s a practical pattern:

    When you first request the token, capture both the token string and its expiry time (usually returned in the response as expires_in or expires_at). Store these in a table or single-instance record:

    • AccessToken: Text
    • TokenCreatedAt: DateTime
    • TokenExpiresIn: Integer (seconds or minutes)
       

    Then, before making any API call, check whether the current time exceeds the expiry threshold:

    al
    procedure IsTokenExpired(): Boolean
    var
        TokenCreatedAt: DateTime;
        TokenExpiresIn: Integer;
        CurrentTime: DateTime;
    begin
        // Retrieve stored values
        TokenCreatedAt := GetTokenCreatedAt();
        TokenExpiresIn := GetTokenExpiresIn();
        CurrentTime := CurrentDateTime();
    
        exit(CurrentTime > TokenCreatedAt + (TokenExpiresIn * 1000)); // Convert seconds to milliseconds
    end;
     
     

    If IsTokenExpired returns true, trigger a refresh or re-authentication flow. You can also add a buffer (e.g., 5 minutes) to avoid edge cases.

    For JWT tokens, if you want to decode the payload and extract the exp claim directly, you’ll need a custom .NET interop or external service, AL alone doesn’t support base64 decoding or JSON parsing of JWT segments.
     

    Helpful Reference
    OAuth token expiry handling – Dynamics Community
    Service-to-service authentication in BC – Andrei Lungu
    Handling token timeouts in API calls – Microsoft Fabric Community
    Authenticating users with Azure AD – Microsoft Learn


    If you find this helpful, feel free to mark this as the suggested or verified answer.

    Cheers
    Jeffrey

  • Suggested answer
    Suresh Kulla Profile Picture
    50,269 Super User 2026 Season 1 on at
    In the past, I managed tokens by storing both the token and its expiration date in a setup table. Whenever I needed to request a new token, I would first check the expiration date in the table. If the token had expired, I would then request a new one. When you request the new token, the response will include an expiration date, which you need to convert and store in the table.
  • Verified answer
    Aman Kakkar Profile Picture
    2,977 Super User 2026 Season 1 on at
    Hi,
     
    I agree with YUN ZHU — if the frequency of your API calls is not very high, the simplest approach is to refresh the token every time you make a request.
     
    However, if the frequency is higher or you have a requirement to store and reuse the token until it expires, you can implement a caching mechanism. 
     

    Here’s a simple, generic approach outline:

    1. Create a table with fields like Code, Access Token and Expires On.

    table 50000 "API Token Cache"
    {
        DataClassification = ToBeClassified;
        fields
        {
            field(1; "Code"; Code[20]) { }
            field(2; "Access Token"; Text[1024]) { }
            field(3; "Expires On"; DateTime) { }
        }
        keys
        {
            key(PK; "Code") { Clustered = true; }
        }
    }
    

    When you request a token from the external API, parse the expires_in value and calculate the expiry timestamp. Before each API call, check whether the token is still valid; if not, refresh it.

        procedure GetBearerToken(Code: Code[20]): Text
        var
            Client: HttpClient;
            Response: HttpResponseMessage;
            Content: HttpContent;
            ContentHeaders: HttpHeaders;
            ResponseText: Text;
            JsonTok: JsonToken;
            JsonObj: JsonObject;
            jTok: JsonToken;
            AccessToken: Text;
            ExpiresIn: Integer;
            ExpiresOn: DateTime;
            TokenCache: Record "API Token Cache";
            TenantID: Text[250];
            ClientID: Text[250];
            ClientSecret: Text[250];
            TokenUrl: Text;
            Payload: Text;
            BufferSeconds: Integer;
            NoResponseError: TextConst ENU = 'No response from authentication endpoint.';
            NoAccessToken: TextConst ENU = 'Access token was not found in response.';
            AccessTokenEmpty: TextConst ENU = 'Access token is empty.';
            MissingClientCredentials: TextConst ENU = 'Missing required credentials in Legal Entity record.';
        begin
            BufferSeconds := 30;
    
            TenantID := '<Your Directory ID>';
            ClientID := '<Your Client ID>';
            ClientSecret := '<Your Secret>';
            if (TenantID = '') or (ClientID = '') or (ClientSecret = '') then
                Error(MissingClientCredentials);
    
            if TokenCache.Get(Code) then begin
                if System.CurrentDateTime() < TokenCache."Expires On" - BufferSeconds * 1000 then
                    exit(TokenCache."Access Token");
            end;
    
            TokenUrl := 'https://login.microsoftonline.com/' + TenantID + '/oauth2/v2.0/token';
            Payload := 'grant_type=client_credentials&client_id=' + ClientID +
                       '&client_secret=' + ClientSecret +
                       '&scope=https://api.businesscentral.dynamics.com/.default';
    
            Content.WriteFrom(Payload);
            Content.GetHeaders(ContentHeaders);
            ContentHeaders.Clear();
            ContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded');
    
            Client.Post(TokenUrl, Content, Response);
            Response.Content().ReadAs(ResponseText);
    
            if ResponseText = '' then
                Error(NoResponseError);
    
            JsonTok.ReadFrom(ResponseText);
            JsonObj := JsonTok.AsObject();
    
            if not JsonObj.SelectToken('access_token', jTok) then
                Error(NoAccessToken);
    
            AccessToken := jTok.AsValue().AsText();
            if AccessToken = '' then
                Error(AccessTokenEmpty);
    
            if JsonObj.SelectToken('expires_in', jTok) then
                ExpiresIn := jTok.AsValue().AsInteger()
            else
                ExpiresIn := 3600;
    
            ExpiresOn := System.CurrentDateTime() + ExpiresIn * 1000;
    
            // update cache (insert or modify)
            if TokenCache.Get(Code) then begin
                TokenCache."Access Token" := AccessToken;
                TokenCache."Expires On" := ExpiresOn;
                TokenCache.Modify();
            end else begin
                TokenCache.Init();
                TokenCache."Code" := Code;
                TokenCache."Access Token" := AccessToken;
                TokenCache."Expires On" := ExpiresOn;
                TokenCache.Insert();
            end;
    
            exit(AccessToken);
        end;
    
     
    I hope it helps.
    Aman K

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Introducing the 2026 Season 1 community Super Users

Congratulations to our 2026 Super Stars!

Congratulations to our 2025 Community Spotlights

Thanks to all of our 2025 Community Spotlight stars!

Leaderboard > Small and medium business | Business Central, NAV, RMS

#1
OussamaSabbouh Profile Picture

OussamaSabbouh 1,926 Super User 2026 Season 1

#2
YUN ZHU Profile Picture

YUN ZHU 1,158 Super User 2026 Season 1

#3
Khushbu Rajvi. Profile Picture

Khushbu Rajvi. 533 Super User 2026 Season 1

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans