### About

This guide provides instructions to create a user in Bloom Enablement Services, and the steps necessary to import that user's tradelines for furnishing. Also included are instructions for closing a user's tradeline, and deleting a user.

### Terminology

- **[Plaid](https://plaid.com/docs/)** - An aggregator. An aggregator handles creating connections to financial institutions and retrieving information about those connections like bank accounts and transactions. Currently, Plaid is the only natively supported aggregator - contact Bloom support if you would like to use a different aggregator.
- **User** - a user in Bloom Enablement Services represents a consumer. A user can have connections and tradelines.
- **Connection** - a connection is used to pull tradeline data from an aggregator into Bloom Enablement Services.
- **Tradeline** - a tradeline represents a user's account with recurring transactions.

### Pre-requisites

- Completed Bloom onboarding process.
- Create a Plaid account. (To use a different aggregator, contact Bloom support.)

### Getting Started

1. Obtain a `Client ID` and `Client Secret` for Authorization from the Bloom onboarding process. An `organizationSlug` is also needed, and obtained during onboarding.

2. Create a user via the [RegisterOrganizationUser](https://developers.bloomcredit.io/page/enablement_gql_ref#mutation-registerOrganizationUser) GraphQL Mutation, with the user's personal information and the `organizationSlug` obtained above. _Optionally, check if a user exists with the email by using the [UserExistsWithEmail](https://developers.bloomcredit.io/page/enablement_gql_ref#query-userExistsWithEmail) GraphQL Query. In Bloom's sandbox environment, there is a limit of 100 users per organization. There is no limit in the production environment._

3. Grab the correct GraphQL URL from [What URL Should I be Using](https://developers.bloomcredit.io/docs/enablement-services-getting-started#what-url-should-i-be-using). Use this URL for all following GraphQL operations.

4. This step requires Organization Scoped Authentication (see [Enablement Services Authentication](https://developers.bloomcredit.io/docs/enablement-services-authentication)).

```undefined
   Authorization: Bearer <organization_scoped_access_token>
   ```

5. The `user_id` in the response needs to be stored against the consumer, and is required to interact with certain Enablement Services endpoints.

> NOTE: If you've already registered [a webhook endpoint](https://developers.bloomcredit.io/docs/enablement-webhooks) you will receive a `user.created` event upon completion of this step

GraphQL

```graphql
   mutation RegisterOrganizationUser {
       registerOrganizationUser(
           input: {
               organizationSlug: "bloom"
               email: "buzz.inga@bloomcredit.io"
               phoneNumber: "+12345678901"
               name: { first: "Buzz", last: "Inga" }
               address: {
                   line1: "123 Main "
                   city: "Athens"
                   state: "FL"
                   country: "USA"
                   zipcode: "37745"
                   type: PRIMARY
               }
               dateOfBirth: { day: 14, month: 6, year: 1986 }
               nationalId: "1234"
           }
       ) {
           user {
               id
               name {
                   first
                   middle
                   last
               }
           }
       }
   }
   ```

6. Authenticate the user via the `/v2/users/<user_id>/authenticate` HTTP endpoint. Use the API URL, _not_ the GraphQL URL, from [What URL Should I Be Using?](https://developers.bloomcredit.io/docs/enablement-services-getting-started#what-url-should-i-be-using). Replace `<user_id>` with the `user_id`from the previous step's response. The access token will be in the response body, to be used for authorizing the user.

Sample Response

```json
   {
     "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30",
     "expires_in": 7200,
     "token_type": "Bearer"
   }
   ```
   
7. This token is valid for 2 hours. Upon expiration, regenerate one following the same process.
8. There may be a slight delay of no more than 10 seconds between user creation, and when authentication can be performed.
9. Use Organization Scoped Authentication, just as in the previous step.

```undefined
   curl --location --request POST 'https://api.bloomplus.dev/v2/users/<user_id>/authenticate' \
   --header 'Authorization: Bearer <organization_scoped_access_token>'
   ```

10. For the remaining steps, use the user scoped access token obtained in Step 3 for authentication unless otherwise stated. Provide the token as a bearer token in the headers. This is referred to as User Scoped Authentication (see [Enablement Services Authentication](https://developers.bloomcredit.io/docs/enablement-services-authentication)). Replace `user_scoped_access_token` with the user's access token.

```undefined
   Authorization: Bearer <user_scoped_access_token>
   ```

11. Check that the newly created user exists with the [Me](https://developers.bloomcredit.io/page/enablement_gql_ref#query-me) GraphQL query. Make sure you are using the User Scoped access token from the previous step for authentication.

GraphQL

```graphql
   query Me {
          me {
              id
              email
              phoneNumber
              status
              notificationPreferences
              createdAt
              updatedAt
              syncedAt
              closedAt
              name {
                  first
                  middle
                  last
              }
          }
   }
   ```

12. Obtain a connection token from Plaid. See [Plaid Processor Tokens](https://developers.bloomcredit.io/docs/plaid-processor-tokens).
13. The best sync times in the Bloom Aggregator occur when the Plaid [SYNC_UPDATES_AVAILABLE](https://plaid.com/docs/transactions/webhooks/#integrating-the-update-notification-webhook) webhook has been received with the field `historical_update_complete` set to `true`. Waiting for this update allows Bloom to import the full transaction history, instead of having to poll for the status periodically (potentially wasting import time).
14. All processor tokens created in Step 12 will be passed into the [ImportPlaidProcessorTokens](https://developers.bloomcredit.io/page/enablement_gql_ref#mutation-importPlaidProcessorTokens) mutation in a single request to import the processor tokens.

This will return two lists. One list contains a list of the created connection object and corresponding token, one for each successfully imported token. The other list contains contains a list of each failed token and a corresponding error code. This will start a workflow in the background to ingest the user's transactions into Bloom Enablement Services. If an error is encountered importing the processor token, the connection's status will be updated. Updates can be viewed using the [Me](https://developers.bloomcredit.io/page/enablement_gql_ref#query-me) query.

> NOTE: If you've already registered [a webhook endpoint](https://developers.bloomcredit.io/docs/enablement-webhooks) you will receive one `connection.created` and one `user.synced` event for each token imported upon completion of this step. You will also receive `tradeline.created` webhooks if the imported tokens have recurring sets of transactions.

GraphQL

```graphql
   mutation ImportPlaidProcessorTokens {
       importPlaidProcessorTokens(
           input: {
               tokens: [
                   "processor-sandbox-448c2232-72d1-4592-92a3-dc1af242fead"
               ]
           }
       ) {
           successes {
               token
               connection {
                   id
                   status
                   externalConnectionId
               }
           }
           failures {
               token
               error
           }
       }
   }
   ```

15. Check that the user's transactions have been imported. These will be listed under the tradelines for the user in the [Me](https://developers.bloomcredit.io/page/enablement_gql_ref#query-me) query, once the ingestion process is completed. The transaction ingestion is complete when the query shows the user's tradelines or once a `user.synced` webhook is received. A sample query is listed below, showing the first 5 tradelines for the user. _In sandbox, this takes about 5 minutes._

Tradelines will be listed as eligible or ineligible via the `isEligible` property. Tradelines with `isEligible == true` and `status == UNOPENED` are able to be opened in the next step.

1. **Opening a tradeline begins the process of reporting it to credit bureaus.**
   2. The user's tradelines have been successfully imported when the user's `syncedAt` property has been set to a non-null date. This property will be updated whenever the user's tradelines are updated.
   3. In the response, the tradeline ID's will be under tradelines > edges > node > id for each tradeline.

GraphQL

```graphql
   query Me {
       me {
           tradelines(input: { first: 5 }) {
               edges {
                   cursor
                   node {
                       id
                       merchant
                       category
                       status
                       isEligible
                       paymentFrequency
                       createdAt
                       updatedAt
                       transactions(input: { first: 5 }) {
                           edges {
                               cursor
                               node {
                                   id
                                   date
                                   value {
                                       amount
                                       currency
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
   }
   ```

16. Open a tradeline to be reported with one of the corresponding open tradeline methods. Each bill type has a corresponding mutation to open the tradeline (ie. rent, utility, telco).
   1. You will need the `tradeline_id` obtained in the previous step as well as some extra details about the tradeline. Only tradelines with `status == UNOPENED` and `isEligible == true` can be opened.
   2. After opening a tradeline the status will change to `OPEN`. Retrieve the status from the response to verify. This tradeline will now be furnished to the bureaus.

> NOTE: If you've already registered [a webhook endpoint](https://developers.bloomcredit.io/docs/enablement-webhooks) you will receive a `tradeline.opened` event upon completion of this step

For example, to open a Utility tradeline:

GraphQL

```graphql
   mutation OpenUtilityTradeline {
     openUtilityTradeline(
       id: "808b1fe0-8015-4df5-8bed-89d4acf68213"
       details: { isPrimaryAddress: true, utilityTypes: [WATER] }
     ) {
       tradeline {
         id
         status
         details {
           ... on UtilityTradelineDetails {
             isPrimaryAddress
             utilityTypes
           }
         }
       }
     }
   }
   ```

### Closing a Tradeline

1. To close a tradeline, use the [CloseTradeline](https://developers.bloomcredit.io/page/enablement_gql_ref#mutation-closeTradeline) Mutation. All that is required is the `tradeline_id`, which can be found with the [Me](https://developers.bloomcredit.io/page/enablement_gql_ref#query-me) query. This mutation requires an access token, as per [Enablement Services Authentication](https://developers.bloomcredit.io/docs/enablement-services-authentication). Following the example in [Getting Started](https://developers.bloomcredit.io/docs/enablement-services-getting-started#getting-started), the mutation may look like:

GraphQL

```graphql
   mutation CloseTradeline {
       closeTradeline(id: "808b1fe0-8015-4df5-8bed-89d4acf68213") {
           tradeline {
             id
             status
             details {
               ... on UtilityTradelineDetails {
                 utilityTypes
                 isPrimaryAddress
               }
             }
           }
       }
   }
   ```

2. This will start a background workflow in the Bloom Enablement Services to ensure the tradeline will not be furnished.
3. After closing a tradeline the status will change to `CLOSED`. Retrieve the status from the response to verify.

### Delete a User

To delete a user, use the [DeleteMe](https://developers.bloomcredit.io/page/enablement_gql_ref#mutation-deleteMe) mutation. Use the user's access token, mentioned in [Enablement Services Authentication](https://developers.bloomcredit.io/docs/enablement-services-authentication).

GraphQL

```graphql
   mutation DeleteMe {
     deleteMe {
       user {
         id
         name {
           first
           middle
           last
         }
       }
     }
   }
   ```

### Disconnecting a Connection

1. Call the [deleteConnection](https://developers.bloomcredit.io/page/enablement_gql_ref#mutation-deleteConnection) GraphQL mutation.
2. Using the access token associated with your plaid processor token, you may wish to call the [Plaid item remove](https://plaid.com/docs/api/items/#itemremove) endpoint. This will invalidate the processor token.

GraphQL

```graphql
   mutation DeleteConnection {
       deleteConnection(id: "813bbced-d62a-4144-9f60-231f41ff5c16") {
           connection {
               id
               status
               externalConnectionId
           }
       }
   }
   ```

### What URL Should I Be Using

- For sandbox environments:
  - API: `https://api.bloomplus.dev`
  - GraphQL: `https://api.bloomplus.dev/v2/gql`
- For production environments:
  - API: `https://api.bloomcredit.com`
  - GraphQL: `https://api.bloomcredit.com/v2/gql`

### Sandbox vs Production

Currently, there is one main difference between the Sandbox and Production environments in Bloom's Enablement Services:

- Sandbox: can import Plaid sandbox processor tokens
- Production: can **not** import Plaid sandbox processor tokens (instead use production tokens)

Data is not shared between sandbox and production environments.
