Overview

All requests to the Bloom API require a valid Oauth2 Access Token to be present in the Authorization header. You can think of this Access Token like an API key, it is what grants you access to the API.

Bloom will provide you with two credentials:

(1) Client ID
and
(2) Client Secret

These two credentials are what you will use to request an Access Token. You will do so using the Oauth2 client_credentials grant flow which will be explained below.

While your provided Client ID is considered a "public" credential, your Client Secret must only be known by your backend application(s). It must never be included in any front-end applications, stored in plaintext, or transmitted across unsecured (non-SSL) connections. Treat it as you would a password.

🚧

Keep your credentials safe!

If at any time you believe your credentials may have been compromised, please let Bloom know immediately so we can rotate them for you.

General Flow

You will gain and maintain access to the API with the following flow:

  1. Use your Client ID and Client Secret to fetch an Access Token
  2. Put the Access Token in the Authorization header of any request you make to the Bloom API
  3. When the token expires fetch a new one and go back to step (2)

1. Retrieving an Access Token

Using Auth V2 Endpoint

Make a POST request to the /oauth2/token endpoint of the authorization URL.

Use the Environments page to find the authorization URL that corresponds to your target environment.

Required Parameters

ParameterValue
client_idYour Client ID
client_secretYour Client Secret
audienceSee the Environments concept page to find the audience that corresponds to your target environment
scopeSpace separated scopes as required to access api endpoints.
Set "data-access:all"for Data Access endpoints.
Set "furnishment:all"for Furnishment endpoints.
Set "data-access:all furnishment:all"for both Data Access and Furnishment endpoints.
grant_typeAlways use client_credentials

📘

Quick tip

Click the Code Generation tab below to get see an example of this request in your language of choice.

{
  "method": "post",
  "url": "https://authn.bloom.dev/oauth2/token",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": {
    "client_id": "<CLIENT_ID>",
    "client_secret": "<CLIENT_SECRET>",
    "audience": "<API_ENVIRONMENT_AUDIENCE>",
    "scope": "<SCOPES_REQUIRED_PER_API_ENDPOINT>",
    "grant_type": "client_credentials"
  }
}
curl --location --request POST 'https://authn.bloom.dev/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=$CLIENT_ID' \
--data-urlencode 'client_secret=$CLIENT_SECRET' \
--data-urlencode 'audience=$AUDIENCE' \
--data-urlencode 'scope=$SCOPE' \
--data-urlencode 'grant_type=client_credentials'
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "$CLIENT_ID");
urlencoded.append("client_secret", "$CLIENT_SECRET");
urlencoded.append("audience", "$AUDIENCE");
urlencoded.append("scope", "$SCOPE");
urlencoded.append("grant_type", "client_credentials");

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: urlencoded,
  redirect: 'follow'
};

fetch("https://authn.bloom.dev/oauth2/token", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));
import requests

url = "https://authn.bloom.dev/oauth2/token"

client_id = "$CLIENT_ID"
client_secret = "$CLIENT_SECRET"
audience = "$AUDIENCE"
scope = "$SCOPE"
payload= f'client_id={client_id}&client_secret={client_secret}&audience={audience}&scope={scope}&grant_type=client_credentials'
headers = {
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
var https = require('follow-redirects').https;
var fs = require('fs');
var qs = require('querystring');

var options = {
  'method': 'POST',
  'hostname': 'authn.bloom.dev',
  'path': '/oauth2/token',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  'maxRedirects': 20
};

var req = https.request(options, function (res) {
  var chunks = [];

  res.on("data", function (chunk) {
    chunks.push(chunk);
  });

  res.on("end", function (chunk) {
    var body = Buffer.concat(chunks);
    console.log(body.toString());
  });

  res.on("error", function (error) {
    console.error(error);
  });
});

var postData = qs.stringify({
  'client_id': '$CLIENT_ID',
  'client_secret': '$CLIENT_SECRET',
  'audience': '$AUDIENCE',
	'scope': '$SCOPE',
  'grant_type': 'client_credentials'
});

req.write(postData);

req.end();
package main

import (
  "fmt"
  "strings"
  "net/http"
  "io/ioutil"
)

func main() {

  url := "https://authn.bloom.dev/oauth2/token"
  method := "POST"

  payload := strings.NewReader("client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&audience=$AUDIENCE&scope=$SCOPE&grant_type=client_credentials")

  client := &http.Client {
  }
  req, err := http.NewRequest(method, url, payload)

  if err != nil {
    fmt.Println(err)
    return
  }
  req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

  res, err := client.Do(req)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer res.Body.Close()

  body, err := ioutil.ReadAll(res.Body)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(string(body))
}

Access Token Response:

Most commonly you should expect to see 200 status code or a 401 status code. The default error schema let's you handle any unexpected errors like 500 internal server error. If you encounter any other errors, please reach out to us.

{
    "access_token": "<ACCESS_TOKEN_WILL_BE_HERE>",
    "scope": "data-access:all furnishment:all",
    "expires_in": 86400,
    "token_type": "Bearer"
}
{
    "error": "string",
    "error_description": "string"
}
{
    "error": "invalid_client",
    "error_description": "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). passwords do not match"
}

🚧

Caching Access Tokens

Please note that when requesting access tokens using your client credentials, your application should use the access token until it expires. The Access Token Response includes an expires_in field which you can use to determine if the token needs to be renewed (this field contains the number of seconds from when the token was created to when it will expire).

If the token is expired, you can issue a new POST request to /oauth2/token to receive a new access token as you normally would for new tokens.

Using Auth V1 Endpoint (sunsetting end of 2024)

Refer instructions: Get Access Token using Auth V1 Endpoint

2. Making API Requests

Now that you have successfully fetched an Access Token, you can make requests to the rest of the Bloom API. Simply add an HTTP Authorization header in the following format to any request you make to the Bloom API:

"Authorization: Bearer <ACCESS_TOKEN>"

Migrating to Auth V2 Endpoint

This change is limited to API Authentication step that is used to obtain an access token. Once you obtain an access token, you can use it the same way as before in Authorization header to access any other Bloom's APIs.

For existing Bloom clients that have been using v1 auth endpoint, your Client ID and Client Secret will remain unchanged, you can still use your current client credentials with auth v2 endpoint to request an access token.

ChangesAuth V2 EndpointExisting Auth Endpoint
Endpoint Path/oauth2/token/oauth/token
Sandbox Domainauthn.bloom.devauth.bloom.dev
Production Domainauthn.bloomcredit.ioauth.bloomcredit.io
Body Parametersclient_id
client_secret
audience
scope
grant_type
client_id
client_secret
audience
grant_type

Please note that we will not be migrating Data Access V1 clients to Auth v2 endpoint as we plan to sunset Data Access V1 endpoints at the end of this year.

Get Access Token using Auth V1 Endpoint (sunsetting end of 2024)

Make a POST request to the /oauth/token endpoint of the authorization URL.

Use the Environments page to find the authorization URL that corresponds to your target environment.

Required Parameters

ParameterValue
client_idYour Client ID
client_secretYour Client Secret
audienceSee the Environments concept page to find the audience that corresponds to your target environment
grant_typeAlways use client_credentials

📘

Quick tip

Click the Code Generation tab below to get see an example of this request in your language of choice.

{
  "method": "post",
  "url": "https://auth.bloom.dev/oauth/token",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": {
    "client_id": "<CLIENT_ID>",
    "client_secret": "<CLIENT_SECRET>",
    "audience": "<API_ENVIRONMENT_AUDIENCE>",
    "grant_type": "client_credentials"
  }
}
curl --location --request POST 'https://auth.bloom.dev/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=$CLIENT_ID' \
--data-urlencode 'client_secret=$CLIENT_SECRET' \
--data-urlencode 'audience=$AUDIENCE' \
--data-urlencode 'grant_type=client_credentials'
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "$CLIENT_ID");
urlencoded.append("client_secret", "$CLIENT_SECRET");
urlencoded.append("audience", "$AUDIENCE");
urlencoded.append("grant_type", "client_credentials");

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: urlencoded,
  redirect: 'follow'
};

fetch("https://auth.bloom.dev/oauth/token", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));
import requests

url = "https://auth.bloom.dev/oauth/token"

client_id = "$CLIENT_ID"
client_secret = "$CLIENT_SECRET"
audience = "$AUDIENCE"
payload= f'client_id={client_id}&client_secret={client_secret}&audience={audience}&grant_type=client_credentials'
headers = {
  'Content-Type': 'application/x-www-form-urlencoded'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
var https = require('follow-redirects').https;
var fs = require('fs');
var qs = require('querystring');

var options = {
  'method': 'POST',
  'hostname': 'auth.bloom.dev',
  'path': '/oauth/token',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  'maxRedirects': 20
};

var req = https.request(options, function (res) {
  var chunks = [];

  res.on("data", function (chunk) {
    chunks.push(chunk);
  });

  res.on("end", function (chunk) {
    var body = Buffer.concat(chunks);
    console.log(body.toString());
  });

  res.on("error", function (error) {
    console.error(error);
  });
});

var postData = qs.stringify({
  'client_id': '$CLIENT_ID',
  'client_secret': '$CLIENT_SECRET',
  'audience': '$AUDIENCE',
  'grant_type': 'client_credentials'
});

req.write(postData);

req.end();
package main

import (
  "fmt"
  "strings"
  "net/http"
  "io/ioutil"
)

func main() {

  url := "https://auth.bloom.dev/oauth/token"
  method := "POST"

  payload := strings.NewReader("client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&audience=$AUDIENCE&grant_type=client_credentials")

  client := &http.Client {
  }
  req, err := http.NewRequest(method, url, payload)

  if err != nil {
    fmt.Println(err)
    return
  }
  req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

  res, err := client.Do(req)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer res.Body.Close()

  body, err := ioutil.ReadAll(res.Body)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(string(body))
}

Access Token Response

{
    "access_token": "<ACCESS_TOKEN_WILL_BE_HERE>",
    "scope": "read:consumers write:consumers read:credit.bloom.score.vantage3...",
    "expires_in": 86400,
    "token_type": "Bearer"
}

🚧

Caching Access Tokens

Please note that when requesting access tokens using your client credentials, your application should use the access token until it expires. The Access Token Response includes an expires_in field which you can use to determine if the token needs to be renewed (this field contains the number of seconds from when the token was created to when it will expire).

If the token is expired, you can issue a new POST request to /oauth/token to receive a new access token as you normally would for new tokens.