How to integrate with Productboard via OAuth2 - developer documentation

Important: This document is meant to describe how to enable OAuth2 authorization with Productboard. It isn’t supposed to serve as general learning resource for OAuth2 protocol. To learn more about OAuth2 itself, please consult another resources, such as oauth.co

Note: Looking for registering an OAuth2 application with us? Read the Registering an OAuth2 application section. Got questions? Reach out to us at [email protected].

Let’s imagine you are working on a product called AwesomeSheet, building next-gen online spreadsheets. You created a script, which makes use of Productboard’s public API to export features data from Productboard to your application. Now you want to enable your users to import their data from Productboard to AwesomeSheet by simply clicking a button. The problem is - how will the script get access to the Productboard data of your user?

One possibility would be to navigate the user to generate a Public API Access Token in the Productboard application and hand it over to you. This approach has several downsides:

  • It’s a multi-step manual process prone to human error and possibly confusing to less technical users
  • The access token is valid forever (unless explicitly revoked by the user). If malicious 3rd party would get hold of it, they could use it for long-term spying on your user’s data
  • The access token would give the script unrestricted access to the whole Productboard API on behalf of the user. So the user would have to blindly trust that the script will use it only for its declared purpose (exporting features) and not let’s say for reading valuable insights from your user’s customers or deleting their data.

To address these issues we are providing you with the ability to allow your users to grant access to their Productboard workspaces to an external application in a safe, human-friendly, transparent, and restricted manner. We achieve this using OAuth2, which is an industry-standard protocol for authorization.

In the rest of this document, we will walk you through the process of enabling this kind of integration, starting with registering your application and ending with automated generation of access and refresh tokens.

Note: only Makers with admin access are able to authorize OAuth2 applications to access their workspaces.

OAuth2 authorization flow

Registering an OAuth2 application

You can register and manage your OAuth2 application at https://app.productboard.com/oauth2/applications. You need an active Productboard user account to access this page.

Getting the authorization code

Once you have the application registered you can trigger the authorization flow for the user. You do this by crafting a special URL your user will visit after clicking on a link/button you present to them. The URL base path will be app.productboard.com/oauth2/authorize and it will contain the following query parameters:

ParameterDescriptionExample ValueRequired
client_idUnique identifier of the application (received after registration) - random stringhqAGVdAy9FZX5Ky5cBHUB2F
shBdSO6eN75tWMX46ZZd
Yes
response_typeThe value is always code to indicate the client expects authorization code in the response.codeYes
redirect_uriOne of the registered redirect URIs (but URL-encoded). Must be an exact match.https%3A%2F%2Fawesomesheet.com%2Foauth2-callback%3Ffoo%3DbarYes
stateAn arbitrary string that will be sent back unchanged in the response. Useful as CSRF prevention and for encoding state (e.g. to redirect users back where they were before starting the authorization process)4agg4zF76rwd3bBMNo
code_challengeBase64-URL-encoded SHA256 hash of code_verifier generated by client. Required if you want to use the PKCE extension.YTQxYjYxOTRjOTEwMWYwNDNjZmE1ZDkzZTQ5YzU3Mz
E1YWRkZDliNjM4NGRiYzMwNDgyN2U2ZDA4Y2RiOGVkZg
No
code_challenge_methodThe value is always S256, to indicate SHA256 hashing was used to generate the code_challenge. Required if you want to use the PKCE extension.S256No

The full URL embedded in a hyperlink can look like this:

<a href="https://app.productboard.com/oauth2/authorize?client_id=hqAGVdAy9FZX5Ky5cBHUB2FshBdSO6eN75tWMX46ZZd
&response_type=code
&redirect_uri=https%3A%2F%2Fawesomesheet.com%2Foauth2-callback%3Ffoo%3Dbar
&state=4agg4zF76rwd3bBM
&code_challenge=YTQxYjYxOTRjOTEwMWYwNDNjZmE1ZDkzZTQ5YzU3MzE1YWRkZDliNjM4NGRiYzMwNDgyN2U2ZDA4Y2RiOGVkZg
&code_challenge_method=S256" target="_blank">Import features from Productboard</a>

After visiting the link, the user will have to select the Productboard workspace they want to authorize your application for and then (re)authenticate into Productboard. Then they will be presented with the authorization screen containing the information about your application, the permissions (scopes) it requires, and Deny and Authorize buttons:

Once they press Authorize (if they press Deny - see Appendix: Error handling) they will be redirected to the redirect URI you registered for your application along with two extra query parameters:

ParameterDescriptionExample Value
codeThe generated authorization codeT-hTGrKxVMJg4kJOdbmS1i7GDlGgmD-GED2kRwBqQ37
stateThe same value as sent in the authorization request (if present)4agg4zF76rwd3bBM

So in our example case, the user could be redirected to the following URL:

https://awesomesheet.com/oauth2-callback?foo=bar&code=T-hTGrKxVMJg4kJOdbmS1i7GDlGgmD-GED2kRwBqQ37&state=4agg4zF76rwd3bBM

Your integration should listen to that URL and grab the authorization code from the query parameters.

Retrieving the access token

From this point, you no longer need any manual action from the user and everything should be automatically handled by your integration. You can exchange the authorization code received in the previous step for an access token by making POST request to app.productboard.com/oauth2/token with the following query parameters:

ParameterDescriptionExample ValueRequired
client_idUnique identifier of the application (received after registration) - random stringhqAGVdAy9FZX5Ky5cBHUB2FshBdSO6eN75tWMX46ZZdYes
client_secretSecret (password) for the application (received after registration) - random stringN2C--2ceRZ7W0alDjx5W10m5wPIg-4PGD7nwrpPjyMeYes
grant_typeThe value is always authorization_code to indicate we are using Authorization code grantauthorization_codeYes
redirect_uriOne of the registered redirect URIs (but URL-encoded). Must be an exact match.https://awesomesheet.com/oauth2-callback?foo=barhttps%3A%2F%2Fawesomesheet.com%2Foauth2-callback%3Ffoo%3DbarYes
codeAuthorization codeT-hTGrKxVMJg4kJOdbmS1i7GDlGgmD-GED2kRwBqQ37Yes
code_verifierString used to generate code_challenge in the authorization code request. Required if you want to use the PKCE extension.zR.emfeaE.vwRTgN-fU67C6gyT.2zDm7VuolFURfo.PtnNBxfjvmu8yKJ1-CBY.elqm6cvorfLNvT27detiWGh-cP0EuE7lIxQFPLoFbZA2nXI9Ib1nyEe875UjEBXzeNo

Example request URL:

https://app.productboard.com/oauth2/token?client_id=hqAGVdAy9FZX5Ky5cBHUB2FshBdSO6eN75tWMX46ZZd
&client_secret=N2C--2ceRZ7W0alDjx5W10m5wPIg-4PGD7nwrpPjyMe
&grant_type=authorization_code
&code=T-hTGrKxVMJg4kJOdbmS1i7GDlGgmD-GED2kRwBqQ37
&redirect_uri=https://awesomesheet.com/oauth2-callback?foo=barhttps%3A%2F%2Fawesomesheet.com%2Foauth2-callback%3Ffoo%3Dbar
&code_verifier=zR.emfeaE.vwRTgN-fU67C6gyT.2zDm7VuolFURfo.PtnNBxfjvmu8yKJ1-CBY.elqm6cvorfLNvT27detiWGh-cP0EuE7lIxQFPLoFbZA2nXI9Ib1nyEe875UjEBXze

If everything goes well, you should get JSON object with the following fields as a response:

FieldDescriptionExample Value
access_tokenAccess token in JWT format. Used for accessing Productboard public API.eyJ0eXAiOiJKV1QiLCJraWQ…
token_typeThe value is always Bearer - indicates the access token should be used as Bearer token in the Authorization headerBearer
expires_inNumber of seconds the access token is valid for from this moment.86400
refresh_tokenRefresh token - random string. Used to get new access token once it expires.4w4_-gHaOVixNuS_naAvqsRTsCuV7wWQgn0jXQYtUhs
refresh_token_expires_inNumber of seconds the refresh token is valid for from this moment.15552000
created_atUNIX timestamp denoting when these tokens were created.1660051050

Example response:

{
   "access_token":"eyJ0eXAiOiJKV1QiLCJraWQiOiIwZjE5YzIyMjFhNmVlOWUwMDk3ZDliOTRkNzU4YWY0NGUyMDU1MDcxOTI2NTdhYzMzMDRhZjA2ZmIxY2MyMGNhIiwiYWxnIjoiUlM1MTIifQ.eyJpYXQiOjE2NjAwNTEwNTAsImlzcyI6ImU0NjljODdjLTI1NWQtNDMwYS05ZjcyLWFjODY5MzBjM2U5NSIsInN1YiI6IjI0ODM0NCIsInJvbGUiOiJhZG1pbiIsImF1ZCI6Imh0dHBzOi8vYXBpLnByb2R1Y3Rib2FyZC5pbmZvIiwidXNlcl9pZCI6MjQ4MzQ0LCJzcGFjZV9pZCI6IjQ2ODg2IiwiZXhwIjoxNjYwMTM3NDUxfQ.GsrpZb5ID6RtThijXed5RBolEByKxtURSmR4uSLaVeibcZpbzFZWBlN2zTFBkKFRqdYpfTlEr0GJrSc5jZOIp27yKIlvprEj2AW0FG9Zoj_N3V5Tv6f48oQ0x0q4PjAPiqQ2aE6swRYymMiufVqqFlpTlnypkUv7bVw1nDqsHG0zYb3Ha6kXHIGEhHR3RPVst1GspRZyqJo_Ml6N1S6dr5rDf6SxPmscopeEcJ1g9oKDjZTJ1A4wYHC33ZT7amE4VkedQcjAY2Gbslp7noAN9RTIW2vujU2eBJVFBye90dVZIxzoJbs5sGvzgOkUfT80Pbim1Zx-mVDBfLuThvZ5Dg",
   "token_type":"Bearer",
   "scope": "releases:read notes:create",
   "expires_in":86400,
   "refresh_token":"4w4_-gHaOVixNuS_naAvqsRTsCuV7wWQgn0jXQYtUhs",
   "refresh_token_expires_in":15552000 
   "created_at":1660051050
}

Important: The access token can then be used to access the Productboard API in the same way as the Public API access token generated inside Productboard.

Warning: You won’t be able to obtain the access or refresh token without another manual authorization by user if you lose them, so make sure you store them securely

Refreshing the access token

The access token is short-lived. Once it expires (you will get 401 - Unauthorized when making an API request), you need to refresh it using the refresh token you got in the previous step.

Warning: If your refresh token expires before you use it, the user will have to manually reauthorize the application.

The endpoint for refreshing the access token is app.productboard.com/oauth2/token and you need to provide the following query parameters:

ParameterDescriptionExample ValueRequired
client_idUnique identifier of the application (received after registration) - random stringhqAGVdAy9FZX5Ky5cBHUB2FshBdSO6eN75tWMX46ZZdYes
client_secretSecret (password) for the application (received after registration) - random stringN2C--2ceRZ7W0alDjx5W10m5wPIg-4PGD7nwrpPjyMeYes
grant_typeThe value is always refresh_token to indicate we are using refresh token grantrefresh_tokenYes
refresh_tokenRefresh token4w4_-gHaOVixNuS_naAvqsRTsCuV7wWQgn0jXQYtUhsYes

You should receive a response in the same format as when Retrieving the access token, but with fresh access and refresh tokens. The refresh token you just used will expire 60 minutes after you use it (buffer in case you experience network issues, so you can retry).

Retrieving token information

To retrieve information about an active access token and its user, you can use the /oauth2/token/info endpoint.

Request

GET <https://app.productboard.com/oauth2/token/info?access_token=><TOKEN>

Request parameters

ParameterDescriptionRequired
access_tokenAn active access tokenYes

Successful response

Response code 200

{
  "application": {
    "uid": "HhmcWDaB37dRzxn-8LbPWIYHttIgAUg5dFJude6OIu8"
  },
  "resource_owner": {
    "name": "Guybrush Threepwood",
    "email": "[email protected]"
  },
  "space": {
    "name": "Mêlée Island",
    "domain": "meleeisland"
  },
  "scopes": [
    "product_hierarchy_data:manage",
    "product_hierarchy_data:read",
    "releases:create",
    "releases:manage",
    "releases:read",
    "users:manage"
  ],
  "expires_in": 80746,
  "created_at": 1682056984
}

Revoking an access token

To revoke an access token you can use the /oauth2/revoke endpoint.

Request

POST <https://app.productboard.com/oauth2/revoke?client_id=><CLIENT_ID>&client_secret=<CLIENT_SECRET>&token=<TOKEN>

Request Parameters

ParameterDescriptionRequired
client_idUnique identifier of the applicationYes
client_secretSecret (password) for the applicationYes
tokenThe active access token that will be revokedYes

Successful Response

Response code 200

{}

OAuth2 applications management

Listing authorized applications for the workspace

To view authorized applications for their workspace, users can go inside their Productboard and head to Settings > Integrations page, where they will find Installed OAuth 2.0 integrations section. Here they can find useful information about the authorized applications - what are they about, what kind of access do they have, who (and when) authorized them for the workspace and when were they last used. Note that the application will be displayed here only after the user clicks the Authorize button and after the application exchanges the received authorization code for the access token.

Revoking access to authorized application

In the section described above, maker with admin access can also revoke access to any authorized application listed there.

Deleting an OAuth2 application

The process of deleting an OAuth2 application for all the users is currently manual as well. Please fill in the deletion form and verify your ownership by sending us the Client ID and Client Secret you received after registration. After the request is approved, we will delete your application.

Appendix

Error handling

We follow the OAuth2 specification when it comes to error handling.

Errors during the initial authorization flow

The error code will be displayed to the user and if it makes sense it will be sent back to the application (to registered redirect URI) in error query parameter. List of the standard error codes is available here.

Errors during the token requests

For errors that happen during server-to-server communication (access/refresh token requests) the error details will be sent encoded as JSON response to the integration (in error field). List of the standard error codes is available here.

Access Scopes

To limit access of the application and to increase the trust of the users, we require each application developer to explicitly state which kind of permissions the application requires. We do this by leveraging the OAuth2 scopes mechanism. The required permissions (scopes) along with their descriptions will be displayed to the user on the authorization screen before they authorize the application.

We generally support 3 categories of scopes:

  • Read scopes - allow to retrieve and list existing data and subscribe to webhooks.
  • Create scopes - allow to create new data.
  • Manage scopes - allow to update and delete existing data.

Below is the full list of the scopes supported at the moment:

ScopeDescriptionAdminMakerContributor
product_hierarchy_data:readThe application will be able to read all hierarchy entities (products, components, and features) and their attributes in your workspace. This includes the relationships between features, custom field values, releases, and Jira metadata.
product_hierarchy_data:createThe application will be able to create all features and attributes in your workspace. This includes releases, roadmaps, and features.
product_hierarchy_data:manageThe application will be able to edit and delete features and attributes in your workspace. This includes releases, roadmaps, and features.
custom_fields:readThe application will be able to read hierarchy entity custom field definitions.
releases:readThe application will be able to read releases and release groups.
releases:createThe application will be able to create releases and release groups.
releases:manageThe application will be able to edit and delete releases and release groups.
notes:createThe application will be able to create notes.
users:readThe application will be able to list users.
users:manageThe application will be able to edit (note creating) users
users_pii:readThe application will be able to read personally identifying information (names, email addresses, etc.) from (note-creating) users.
members_pii:readThe application will be able to read personally identifying information (names, email addresses, etc.) from your Productboard users.
plugin_integrations:manageThe application will be able to introduce and manage a UI plugin on your Productboard space.
objectives:readThe application will be able to read objectives.
objectives: createThe application will be able to create new objectives.
objectives: manageThe application will be able to edit and delete objectives.

Note: Some Productboard user roles are allowed to authorize only a subset of the OAuth2 scopes. For a Productboard user to be able to authorize an OAuth2 application, their role must be allowed to authorize all the scopes the application is requesting.

Example 1: If a Maker tries to authorize an OAuth2 application requesting notes:createand users:read scopes, it will work as the Maker role is allowed to authorize both scopes.

Example 2: If a Maker tries to authorize a different application requesting notes:create, users:read and releases:manage scopes, it will fail because the Maker role is not allowed to authorize the releases:manage scope.

Credentials Expirations

For security reasons, most of the credentials used within the OAuth2 authorization flow will expire after some time, and you need to refresh them. Below is the table listing all the credentials and their expiration times.

CredentialExpirationHow to refresh it
Client SecretNever-
Authorization code10 minutes (or immediately after being used)Manual reauthorization by user
Access token24 hoursRefresh token
Refresh token180 days (or 60 minutes after being used)Refresh token / Manual reauthorization by user

Feedback

If you have any questions or feedback regarding OAuth2 in Productboard, please reach us at [email protected].