Token Endpoint

[ POST ] /oauth2/v1/token

The OAuth token endpoint is used to obtain access tokens. An access token must be provided as a bearer token with any request to an API used to read, write, or update data in athenaOne.

The sequence for calling the token endpoint is different for apps using 2-legged OAuth versus 3-legged OAuth: An app using 2-legged OAuth can call the token endpoint directly to obtain an access token, while an app using 3-legged OAuth must first call the authorize endpoint to request and obtain consent from an end user, such as a provider or patient. As a byproduct of end user authorization, the token endpoint response for 3-legged OAuth may also contain an ID token, refresh token, or launch context (see Output Parameters & Token Types section below for details), whereas only an access token is returned for 2-legged OAuth.

We support 4 methods of authenticating apps against the token endpoint, some of which are exclusive to your app’s authorization method and selected app type when registering your application. Refer to the sections below for details on token endpoint authentication using each method:

  • Using a client secret is the simplest method, which requires you to provide your client ID and secret in a basic authorization header when calling the token API.
  • Using an imported public JWKS (JSON Web Key Set) (2-legged OAuth and 3-legged OAuth web apps only) is more complex, but also more secure. A JWT (JSON Web Token) is generated and signed with a public/private keypair by the caller and presented to the token endpoint. The call is authenticated if the JWT's signature can be verified using one of the app's public keys.
  • Using a self-hosted JWKS URL (2-legged OAuth and 3-legged OAuth web apps only) is potentially the most secure method by giving you direct control over the JWKS and key rotation. The overall pattern for JWT generation and authentication follows that of an imported public JWKS, except that JWT signature and verification references a self-hosted URL as opposed to a public one.
  • Using a proof key for code exchange, or “PKCE” – pronounced “pixie” (3-legged OAuth only), requires you to generate your own secret for each token request that can be verified by athenahealth’s authorization server. PKCE is required by the token API for native or single-page browser apps that cannot securely store a client secret.

Token Authentication Using Client Secret

When using secret-based authentication, your app will have a client secret associated with its client ID. This secret will be passed in a basic authorization header on every request to the token endpoint.

Please remember that as a security measure, you should never share your client ID or secret. If this happens, we will need to create another set of credentials associated with your account.

Token Generation

To request a token using secret-based authentication, make a request with the following parameters utilizing basic authentication to include your client ID and client secret in a basic authorization header.

curl \ 
-X POST \ 
-d "grant_type=client_credentials" \ 
-d "scope=athena/service/Athenanet.MDP.*"


There is a common error that involves POSTing the username and password. This does not work. It must use basic authentication (essentially, "$key:$secret" Base64 encoded).

Token Authentication Using JWKS (JSON Web Key Sets)

Client-assertion JWT-based (JSON Web Token) authentication uses a public/private keypair to authenticate the caller to the token endpoint (see here for information on how to generate public/private keypairs). Using this approach, your application will have no client secret. You must associate at least one public key with your application via a self-hosted JWKS (JSON Web Key Set) URL or by importing your public JWKS.

JWT-based authentication with JWKS is more involved for the end-user, requiring you to generate keypairs and sign JWT assertions to retrieve a token. The benefit of using this method is that it is more secure as the private portion of your keypair is never out of your hands (note: using the self-hosted JWKS URL provides even further control and security versus importing your public JWKS by granting you ownership of your keys and key rotation). Using JWKS also enables zero-downtime rotation of credentials. You can have multiple public keys associated with your app (recommended to have 1-5 keys) enabling you to rotate by introducing a new keypair, transitioning your code to use this new keypair, and then deleting the old keypair.

Forming the JWT Assertion

Requesting a token using the client assertion JWT-method requires that you sign a JWT assertion using the private portion of a keypair associated with your app. This assertion is then provided to the token endpoint for authentication. The assertion JWT must contain the following claims:





Required. The URL of the backend authorization resource. This varies by environment, listed below.

  • Preview:
  • Production:



Required. An expiration time for this token in seconds since the epoch. Must be less than an hour into the future. The assertion token will no longer be valid after this time expires.



Required. Set to the client ID of the application for which you're requesting a token.



Required. Set to the client ID of the application for which you're requesting a token.


Constructing the Client Assertion JWT

Generate a JWT by including the required claims as indicated above. An example is provided below in javascript using the njwt library. Similar libraries exist in most languages. A collection of libraries is listed on the site. Take care when considering the use of a third-party package.

const njwt = require("njwt");  

const fs = require("fs");  


// Some packages require the key in pem format, you can use a package such as  

// pem-jwk to convert.  

const privateKey = fs.readFileSync("private.pem");  

const clientId = "<Your Client ID>";  

const now = Math.floor(new Date().getTime() / 1000);  

const expire = new Date((now + 300) * 1000);  


// Set the environment-appropriate audience claim to the appropriate URL.  

const claims = {  

  aud: "<Environment-Appropriate URL>",  



const jwt = njwt  

  // Provide the algorithm you configured your key with.  

  .create(claims, privateKey, "RS256")  

  .setHeader("kid", "<The kid of the key used in signing>")  






Requesting the Token

To request a token with the JWT you've generated, include the JWT along with the other parameters as demonstrated below:

# Construct the client assertion JWT using a script like the above.  

JWT=$(node generate-jwt.js)  


# Include the JWT in a token request with the appropriate parameters for the 

# environment. 

curl -X POST "" \  

  -H "Accept: application/json" \  

  -H "Content-Type: application/x-www-form-urlencoded" \  

  -d "grant_type=client_credentials" \  

  -d "scope=athena/service/Athenanet.MDP.*" \  

  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \  

  -d "client_assertion=$JWT"    

Token Authentication Using PKCE

Consistent with OAuth best practice and SMART App Launch v2.0.0 proposals, token authentication using PKCE is required by athenahealth’s authorization server for native or single-page browser apps using 3-legged OAuth that cannot securely store a client secret. PKCE is an enhancement to the OAuth 2.0 Authorization Code Flow that requires your app to generate its own secret for each token request (called a “code verifier”) that can be verified by athenahealth’s authorization server (using a transformed value of the code verifier called a “code challenge”, which is passed in the authorize request along with the transformation method, or “code challenge method”).

To leverage PKCE for your app, please refer to the IETF’s official PKCE overview and Okta’s implementation documentation for details and restrictions of the code verifier, code challenge, and code challenge method parameters.

Input Parameters

IMPORTANT: Endpoint requires client authentication. Refer to samples in sections above for token authentication method applicable to your app.

* = required

grant_type* string

For 2-legged OAuth, the value is always client_credentials.

For 3-legged OAuth, either the value is authorization_code to initially request an access token, or the value is refresh_token to obtain a new access token from a refresh token.



Required if grant_type is authorization_code (3-legged OAuth only). The value is the authorization code returned by the authorize endpoint.



Required if grant_type is authorization_code (3-legged OAuth only). The value is the post-login redirect URI for your app and must match the value for redirect_uri provided to the authorize endpoint.



Required if grant_type is authorization code (3-legged OAuth only).



Required if grant_type is authorization code (3-legged OAuth only). The value is the code parameter returned by the authorize endpoint.



Required for token authentication using PKCE if grant_type is authorization_code (3-legged OAuth only) and your app cannot securely store or does not have a client secret. The value is a one-time use, unique string generated by your app, conforming to Okta’s code verifier requirements and for which the transformed value (code challenge) and transformation method (code challenge method) were provided to the authorize endpoint.



Required if grant_type is refresh_token. The value is the refresh token returned by the authorize endpoint.



Required if grant_type is client_credentials (2-legged OAuth) or refresh_token (3-legged OAuth only). The value is a space-delimited, case-sensitive string of requested scopes. If grant_type is refresh_token, the requested scopes must be a subset of those provided to the authorize endpoint when the refresh token was originally generated.

Output Parameters & Token Types

The token endpoint returns a JSON object including up to 3 types of JWTs and associated information:

  • access_token - JWT to be used as a bearer token in subsequent API requests
  • token_type – describes the access token; value is always “Bearer”
  • expires_in - represents the lifetime of the access token in seconds (default is 60 minutes for 2-legged OAuth and 3-legged OAuth apps)
  • id_token – JWT containing information on the authenticated end user (3-legged OAuth only)
  • refresh_token – JWT used to obtain a new access token without requiring re-authentication by an end user (3-legged OAuth only; default expiration is 90 days from the most recent token request that used or generated that refresh token)

If using 2-legged OAuth, the token response has the following format:


  "access_token": "bSQeVaRd47Tnof8GWbDZTud9ghLP", 

  "expires_in": "300" 


If using 3-legged OAuth, the token response has the following format:

{"token_type":"Bearer","expires_in":300,"access_token":"N09411aa6CiXmdNnkp6l7V6lk5UH","scope":"openid fhirUser offline_access launch/patient patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/ patient/","refresh_token":"uw-4cwXncULPI5skwLhdIntJAVc8WeEvPpYcSZDj6PU","id_token":"eyJraWQiOiJ1Z09JUjBWSWNtVnBUejdzeFNPNmpzVk1LQXVPY3ZSOHVnY0ZsbUxMNXFRIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHVibXVjem96bmFMcGFhVzI5NyIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9hdGhlbmEub2t0YS5jb20vb2F1dGgyL2F1czJoZmY1ZXFGYjdXcWZoMjk3IiwiYXVkIjoiMG9hYXQxZHpkc3dFZ2JjSHkyOTciLCJpYXQiOjE2NTA1NTgxNDEsImV4cCI6MTY1MDU2MTc0MSwianRpIjoiSUQudmpRQmJ1N0U4b0tzUUtZMXg1bVNnczN2aVRISXdSeWNhclRHRTVYOFIxSSIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBvNnd6c2F2ZjdVNXpsNFMyOTYiLCJhdXRoX3RpbWUiOjE2NTA1NTgxMjIsImF0X2hhc2giOiJ0WEFFTTdKdF9zaC1rcE50d011cWlRIiwiZmhpclVzZXIiOiJodHRwczovL2FwaS5wbGF0Zm9ybS5hdGhlbmFoZWFsdGguY29tL2ZoaXIvcjQvUGF0aWVudC9hLTEyMy5FLTQ1Njc4OTAifQ.oSmOlQ41imNhulLhYUYAIXzu-nHukethXIuZNaOGrptCjKbcMzIghSo63tR_KW2ROgwgkQ_xhmrjo3NwoIt9eMSDArPb_zvM94RofP0SpcF5p0K-GedWC6HKEFFsB2rwoy9BZ5405Upp6MpY1Xd-nKxaxjXaiNjmwB2qIyt7LrhfZpbPHkYdnma88IYA7AQCIvHRcUZmQ4OK_6RTxIacsp54Rhuc2hcPpUNNKrTxgdCdfXmLhLtB_LlP2kAMKvNlC-MBsvHRrlDUvIhbjioFniY7gEkx1o7M1ezfqAQXwDwMmlbLuTO4ry48Oz4fkOHeGhb-VBoHHrkx-VMyI2K-ag","patient":"a-123.E-4567890"}

The “patient” object at the end of the sample 3-legged response is referred to as the launch context and is exclusive to token requests made as part of a SMART App Launch sequence. To learn more about the launch context, refer to the section below.

Launch Context

The token endpoint response from a SMART App Launch sequence will also return a launch context, which is an additional object specifying from where or for whom the app was launched. Note that the launch context is returned alongside, and not within, any JWTs returned by the token endpoint.

In a Patient Standalone SMART App Launch sequence, the launch context is simply a “patient” object containing the patient ID specified for access by the end user.

In a Provider EHR SMART App Launch sequence, the launch context specifies information on the location in the EHR from which the app was launched, in addition to SMART styling information. For more information on provider launch context, please refer to the Embedded Apps reference documentation on our Developer Portal.

Use an Access Token as a Bearer Token

Your app must parse the token API response for the access token to include in the header of subsequent requests to API endpoints used to read, write, or update athenaOne data. For example:

 curl “” \ 
  -H”Authorization: Bearer {access_token}” 

Refresh a Token

Due to the short-lived nature of access tokens, apps using 3-legged OAuth may want to request these tokens periodically without repeatedly requiring the end user (patient or provider) to log in and approve the request. This is possible using a refresh token, which is returned by the authorize endpoint when the offline_access scope is requested and obtained with the user’s authorization. To get an access token from a refresh token, you will need to form a new token request providing the refresh token in the refresh_token parameter and refresh_token as the value for the grant_type parameter. Your app will also need to specify in the scope parameter which of the previously granted scopes your app would like included in the new access token. You can view which scopes were previously granted with an access token using introspection.

Was this information helpful? Yes | No Thank you for your feedback! What went wrong? Incomplete or incorrect information | Irrelevant Content | Others

On this Page