Managing held data on the Platform API

With great power comes great responsibility, or so that friendly neighborhood guy is always saying (or maybe his uncle?). But the reverse is also true: Great responsibilities require great power. That’s what our partner-managed resources provide. Rather than relying on our aggregation services (which are great, don’t get us wrong) to bring in account and transaction data about your own customers, you can manage all that yourself — down to the last detail — so you can provide customers with the best possible experience. And since you’re in total control of the data, you’re taking your responsibility as stewards very seriously.

Of course, that means there’s some work to do, but that’s what this guide is for. We’ll take you through the process of adding one of your own transactions using the Platform API, as well as doing all the other things you need to do before you get to that point. We’ll also outline some best practices and cover some common problems you might run into.


The MX data architecture

For the purposes of this guide, we’ll assume that what you’re really interested in is transaction data; you want to send a transaction to the MX platform so that it ultimately ends up in front of the eyes of your customer — whether that’s in a UI you build yourself or through one of our products like MoneyMap, Pulse, or FinStrong. Whatever the case, there are a few things you absolutely have to do before we can get that transaction into our system.

First things first: The ability to manage your own data on the Platform API is a premium feature, and you’ll need to have it enabled by MX before you can do any of the stuff in this guide. If it’s not yet enabled, reach out to our support team.

Also, every resource on the MX platform has to fit into our existing data architecture. For instance, every transaction belongs to an account, every account belongs to a member, and finally every member belongs to a user. So before we can deal with the transactions we’re most interested in, we need to deal with users, members, and accounts — in that order.

You’ll also want to think about what your ultimate use case is going to be. Each of these four resources (users, members, accounts, and transactions) have lots of attributes that you can set, but not all of those attributes will be useful for every case, and some of them depend on other attributes, etc. So you’ll need to know exactly what information you plan on sending us and how it’s going to fit into the product you’re building with the Platform API.

Ok. Let’s get going.


Workflows

With all that in mind, here’s a general workflow for creating your first transaction with your held data:

  1. Create a user (which represents your customer);
  2. Create a member belonging to that user (which represents the connection between the user and your special institution);
  3. Create an account (which represents the account the customer holds with you);
  4. Create a transaction (which represents any instance in which money moved into or out of your customer’s account).

Other workflows

Once all those resources exist, workflows are mostly about updating accounts and transactions to reflect the latest information — like increasing an account balance after a paycheck is deposited, or, for that matter, creating a new transaction to represent that deposited paycheck.


1. Create a user

Make a POST request to the create user endpoint, as shown below.

It’s a good idea to send along a unique id of your choice with your request. The API will also give each new user an MX-defined guid (or user_guid when appearing outside the user object). Between your id and the guid, you’ll easily be able to map between your system and ours.

You’ll need the user guid for nearly every request on the MX API, at least when using basic authorization.

You may also include metadata, such as the date the user was created, the end user’s name, etc. Just make sure not to include any sensitive information here, like credentials.

Parameters

Field Required?
email No
id No
is_disabled No
metadata No
1
2
3
4
5
6
7
8
9
10
11
12
curl -i -X POST 'https://int-api.mx.com/users' \
-u 'client_id:api_key' \
-H 'Accept: application/vnd.mx.api.v1+json' \
-H 'Content-Type: application/json' \
-d '{
      "user": {
        "id": "partner-2345",
        "is_disabled": false,
        "email": "totally.fake.email@notreal.com",
        "metadata": "Yada yada yada"
      }
    }'
1
2
3
4
5
6
7
8
9
{
  "user": {
    "email": "totally.fake.email@notreal.com",
    "guid": "USR-11141024-90b3-1bce-cac9-c06ced52ab4c",
    "id": "partner-2345",
    "is_disabled": false,
    "metadata": "Yada yada yada"
  }
}

2. Create a managed member


2.1 List the available institutions

This endpoint lists the institutions which you are allowed to use for creating managed members. Why are we making this request? Because you’re going to need the institution_code that gets returned for step 2.2, where we’ll actually create the partner-managed member.

For most partners, there will likely be only one available institution, but others may have a setup that requires more. You’ll figure all that out with MX when you’re getting managed data enabled. At any rate, you shouldn’t need to perform this step very often since your list of available institutions won’t change very often, and you should be able to cache this small list.

Endpoint:

GET /managed_institutions

1
2
3
curl -i 'https://int-api.mx.com/managed_institutions' \
-u 'client_id:api_key' \
-H 'Accept: application/vnd.mx.api.v1+json'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
  "institutions": [
    {
      "code": "partnerinstitutioncode",
      "medium_logo_url": "https://content.moneydesktop.com/storage/MD_Assets/Ipad%20Logos/100x100/default_100x100.png",
      "name": "Partner Institution",
      "small_logo_url": "https://content.moneydesktop.com/storage/MD_Assets/Ipad%20Logos/50x50/default_50x50.png",
      "supports_account_identification": false,
      "supports_account_statement": false,
      "supports_account_verification": false,
      "supports_oauth": false,
      "supports_transaction_history": false,
      "url": "https://PrtnerInstitutionWebsite.com"
    },
    {
      "code": "mxbank",
      "medium_logo_url": "https://content.moneydesktop.com/storage/MD_Assets/Ipad%20Logos/100x100/INS-1572a04c-912b-59bf-5841-332c7dfafaef_100x100.png",
      "name": "MX Bank",
      "small_logo_url": "https://content.moneydesktop.com/storage/MD_Assets/Ipad%20Logos/50x50/INS-1572a04c-912b-59bf-5841-332c7dfafaef_50x50.png",
      "supports_account_identification": true,
      "supports_account_statement": false,
      "supports_account_verification": true,
      "supports_oauth": false,
      "supports_transaction_history": true,
      "url": "https://www.mx.com"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 30,
    "total_entries": 2,
    "total_pages": 1
  }
}

2.2 Create a member

Now that we’ve got a user_guid and your institution_code, we can create a member. As with creating a user — really, with every object — it’s strongly encouraged to give the member a unique id so you can match the data on your systems with our systems.

If the request is successful, the response will contain the newly created member.

Here’re the parameters you can include in your request. See the API reference for more detailed information on each parameter.

Parameters

Field Data type Required?
id String No
institution_code String Yes (This should always be set to your default institution.)
metadata String No
name String No

Endpoint:

POST /users/{user_guid}/managed_members

1
2
3
4
5
6
7
8
9
10
11
curl -i -X POST 'https://int-api.mx.com/users/USR-11141024-90b3-1bce-cac9-c06ced52ab4c/managed_members' \
  -u 'client_id:api_key' \
  -H 'Accept: application/vnd.mx.api.v1+json' \
  -H 'Content-Type: application/json' \
  -d '{
        "member": {
            "institution_code": "mxbank",
            "id": "partner-1234",
            "metadata": "this and that"
        }
      }'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "member": {
    "aggregated_at": null,
    "connection_status": "CONNECTED",
    "guid": "MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e",
    "id": "partner-1234",
    "institution_code": "aef89293-88d9-42e6-82a2-0a2b14fe7667",
    "is_being_aggregated": false,
    "is_managed_by_user": false,
    "is_oauth": false,
    "metadata": "this and that",
    "name": null,
    "successfully_aggregated_at": null,
    "user_guid": "USR-11141024-90b3-1bce-cac9-c06ced52ab4c",
    "user_id": "partner-2345"
  }
}

3. Create a managed account

Now we have a member, so it’s time to create an account.

Remember that a member represents the overall connection between your institution and the user, so if they open a new account with you, you don’t want to create a new member; you want to attach a new account to the member you just created. In other words, members can have multiple accounts, each one representing an account that user has with you: one for their savings account, another for their checking, another for their mortgage, etc.

There are lots of pieces of information attached to an account, with the most important for users probably being balance — at least for most common integrations and use cases. At any rate, you can set all of these fields when you create an account and update them later if they change, like a balance inevitably will.

See the API reference for all the information you’ll need for each of these fields, as well as what enums are available for things like type and subtype.

Parameters

Parameters Required?
account_number No
apr No
apy No
available_balance No
available_credit No
balance Yes
cash_surrender_value No
credit_limit No
currency_code No
day_payment_is_due No
death_benefit No
id No
interest_rate No
is_closed No
is_hidden No
last_payment_at No
last_payment No
loan_amount No
matures_on No
metadata No
minimum_balance No
minimum_payment No
name Yes
nickname No
original_balance No
payment_due_at No
payoff_balance No
routing_number No
started_on No
subtype No
type Yes

Endpoint:

POST /users/{user_guid}/managed_members/{member_guid}/accounts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curl -i -X POST 'https://int-api.mx.com/users/USR-11141024-90b3-1bce-cac9-c06ced52ab4c/managed_members/MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e/accounts' \
-u 'client_id:api_key' \
-H 'Accept: application/vnd.mx.api.v1+json' \
-H 'Content-Type: application/json' \
-d'{
      "account": {
        "account_number": "12345",
        "balance": "10000",
        "id": "partner-account-7890",
        "is_closed": false,
        "metadata": "some metadata",
        "name": "Swiss Bank Acct",
        "nickname": "Swiss Account",
        "started_on": "1980-03-28",
        "subtype": "MONEY_MARKET",
        "type": "SAVINGS"
      }
    }'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
  "account": {
    "account_number": "12345",
    "apr": null,
    "apy": null,
    "available_balance": 10000.00,
    "available_credit": null,
    "balance": 10000.00,
    "cash_balance": null,
    "cash_surrender_value": null,
    "created_at": "2021-08-31T18:17:44Z",
    "credit_limit": null,
    "currency_code": null,
    "day_payment_is_due": null,
    "death_benefit": null,
    "guid": "ACT-1e194555-a690-44ec-b363-282165cf4dab",
    "holdings_value": null,
    "id": "partner-account-7890",
    "institution_code": "mxbank",
    "interest_rate": null,
    "insured_name": null,
    "is_closed": false,
    "is_hidden": false,
    "last_payment": null,
    "last_payment_at": null,
    "loan_amount": null,
    "matures_on": null,
    "member_guid": "MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e",
    "metadata": "some metadata",
    "minimum_balance": null,
    "minimum_payment": null,
    "name": "Swiss Bank Acct",
    "nickname": null,
    "original_balance": null,
    "pay_out_amount": null,
    "payment_due_at": null,
    "payoff_balance": null,
    "premium_amount": null,
    "routing_number": null,
    "started_on": "1980-03-28",
    "subtype": "MONEY_MARKEY",
    "total_account_value": null,
    "type": "SAVINGS",
    "updated_at": "2021-08-31T18:17:44Z",
    "user_guid": "USR-11141024-90b3-1bce-cac9-c06ced52ab4c"
  }
}

4. Create a managed transaction

Now we can get to (probably) the most important thing, the long point of this guide: The transaction.

Like an account, there is a lot of information you can store on a transaction to accommodate lots of different possible use cases and situations. We created a savings account above, so this transaction will probably be one of the more ordinary transactions: a simple deposit.

Parameters

Field Data type Required?
amount String Yes
category String No
category_guid String No
check_number_string String No
currency_code String No
description String Yes
id String No
is_international String No
latitude String No
localized_description String No
localized_memo String No
longitude String No
memo String No
metadata String No
merchant_category_code String No
merchant_guid String No
merchant_location_guid String No
posted_at String No
status String Yes
transacted_at String Yes
type String Yes

Endpoint:

POST /users/{user_guid}/managed_members/{managed_member_guid}/accounts/{account_guid}/transactions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curl -L -X POST 'https://int-api.mx.com/users/USR-11141024-90b3-1bce-cac9-c06ced52ab4c/managed_members/MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e/accounts/ACT-09c0ee66-51a8-4edf-b977-99534a471134/transactions' \
  -u 'client_id:api_key' \
  -H 'Accept: application/vnd.mx.api.v1+json' \
  -H 'Content-Type: application/json' \
  -d '{
        "transaction": {
            "id": "partner-transaction-3QP5X0",
            "status": "PENDING",
            "type": "CREDIT",
            "amount": 400,
            "description": "Savings Deposit",
            "is_international": false,
            "transacted_at": "2021-09-01T12:00:00Z",
            "category": "INCOME",
            "latitude": 40.429675,
            "longitude": -111.891982
        }
      }'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
  "transaction": {
    "account_guid": "ACT-1e194555-a690-44ec-b363-282165cf4dab",
    "account_id": "1",
    "amount": 8.2,
    "category": "Gas",
    "category_guid": "CAT-b6d63a19-30a7-e852-2703-bdfb4072289e",
    "check_number_string": "9543",
    "created_at": "2020-06-28T12:00:00Z",
    "currency_code": "USD",
    "date": "2020-03-02",
    "description": "Costcoo Gas",
    "guid": "TRN-181ce5d9-d33e-45b9-a9cd-8f8ef9d82d5d",
    "id": "T-3QP5X0",
    "is_bill_pay": false,
    "is_direct_deposit": false,
    "is_expense": false,
    "is_fee": false,
    "is_income": true,
    "is_international": false,
    "is_overdraft_fee": false,
    "is_payroll_advance": false,
    "is_recurring": false,
    "is_subscription": false,
    "latitude": "40.429675",
    "localized_description": null,
    "localized_memo": null,
    "longitude": "-111.891982",
    "member_guid": "MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e",
    "memo": "POS Purchase",
    "merchant_category_code": null,
    "merchant_guid": "MCH-bcd4eed1-f341-b7bb-4cbd-e2a854205306",
    "merchant_location_guid": null,
    "metadata": null,
    "original_description": "COSTCO GAS STATION #22299",
    "posted_at": null,
    "status": null,
    "top_level_category": "Car & Auto",
    "transacted_at": "2020-06-28T12:00:00Z",
    "type": "DEBIT",
    "updated_at": "2020-06-28T12:00:00Z",
    "user_guid": "USR-11141024-90b3-1bce-cac9-c06ced52ab4c",
    "user_id": "U-39XBF7"
  }
}

5. Updating resources

Now you’ve accomplished most of the common tasks you’ll need to manage your own held data with the Platform API. Updating resources is another one, Whether that’s changing a transaction from PENDING to POSTED, updating an account balance, updating a user’s email address, or something else.

We’ll cover updates here with one example: Let’s update the account we created so its balance reflects the deposit we just recorded by creating a new transaction. We could show you examples of updating all the various resources, but they’re fundamentally similar to updating an account, so we won’t bore you with a bunch of extra stuff.

Just keep a couple things in mind when updating any resource: You can include any of the optional parameters we listed above (and in the API reference), so no specific field is “required,” in a certain sense — but the request body can’t be empty. In other words, if you make an update request, you need to include some kind of data or else you’ll get an error.

The account balance was $10,000 before, and we just deposited $400. Let’s get that balance updated.

Endpoint:

PUT /users/{user_guid}/managed_members/{member_guid}/accounts/{account_guid}

1
2
3
4
5
6
7
8
9
curl -i -X PUT 'https://int-api.mx.com/users/USR-11141024-90b3-1bce-cac9-c06ced52ab4c/managed_members/MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e/accounts/ACT-1e194555-a690-44ec-b363-282165cf4dab' \
-u 'client_id:api_key' \
-H 'Accept: application/vnd.mx.api.v1+json' \
-H 'Content-Type: application/json' \
-d '{
      "account": {
        "balance": 10400.00
      }
    }'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
  "account": {
    "account_number": "12345",
    "apr": null,
    "apy": null,
    "available_balance": 10000.00,
    "available_credit": null,
    "balance": 10400.00,
    "cash_balance": null,
    "cash_surrender_value": null,
    "created_at": "2021-08-31T18:17:44Z",
    "credit_limit": null,
    "currency_code": null,
    "day_payment_is_due": null,
    "death_benefit": null,
    "guid": "ACT-1e194555-a690-44ec-b363-282165cf4dab",
    "holdings_value": null,
    "id": "partner-account-7890",
    "institution_code": "mxbank",
    "interest_rate": null,
    "insured_name": null,
    "is_closed": false,
    "is_hidden": false,
    "last_payment": null,
    "last_payment_at": null,
    "loan_amount": null,
    "matures_on": null,
    "member_guid": "MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e",
    "metadata": "some metadata",
    "minimum_balance": null,
    "minimum_payment": null,
    "name": "Swiss Bank Acct",
    "nickname": null,
    "original_balance": null,
    "pay_out_amount": null,
    "payment_due_at": null,
    "payoff_balance": null,
    "premium_amount": null,
    "routing_number": null,
    "started_on": "1980-03-28",
    "subtype": "MONEY_MARKEY",
    "total_account_value": null,
    "type": "SAVINGS",
    "updated_at": "2021-09-01T18:20:37Z",
    "user_guid": "USR-11141024-90b3-1bce-cac9-c06ced52ab4c"
  }
}

6. Deleting resources (and what to avoid)

Ideally, deleting something is not the best solution. Keeping data around is usually good, so it can be restored easily if it needs to be. Marking an account as closed or temporarily disabling a user might be a better idea. But deletes are necessary sometimes, for sure.

Deleting something is an extremely simple request, so really what you need to know is what not to do.

Deletes on the MX platform cascade; when you delete an account, it also deletes every transaction in that account — even if there are thousands. If you delete a user, it deletes every member on that user, and every account on every member, and every transaction on every account, etc. Basically, one delete on your end could mean thousands on our end.

So the basic idea is be careful. Deleting a transaction is fine. Deleting a user is also fine. But deleting 1,000 users all at once is probably a bad idea. If you need to make a lot of deletes that will result in a lot of cascades, just reach out to us and we’ll be able to accommodate it. We just don’t want to be caught off guard.

Here’s an example for deleting a member. You won’t get a response body, but the status will be 204 No Content if it’s successful.

Endpoint:

DELETE /users/{user_guid}/managed_members/{member_guid}

1
2
3
curl -i -X DELETE 'https://int-api.mx.com/users/USR-11141024-90b3-1bce-cac9-c06ced52ab4c/managed_members/MBR-e6239750-24e8-4833-93a8-d4c2ecf2ec7e' \
  -u 'client_id:api_key' \
  -H 'Accept: application/vnd.mx.api.v1+json'