NAV
Ruby Python Curl JavaScript

Spinwheel APIs v1.0

Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.

Welcome to Spinwheel's documentation! Our mission is to make it easy for you to quickly and securely stand up all manner of liability management capabilities inside your own application. Broadly speaking, we expose our services using two different channels.

The first is through what we call Drop-In Modules or DIMs. DIMs are ready-made components that can be integrated directly into your application. Each module exposes a discreet unit of Spinweel functionality—that was a mouthful, but to illustrate by way of example, our LoanPal module provides users with an intuitive dashboard where they can view both aggregate and granular details of their student loans as well as access their transaction history. DIMs are incorporated into your app by way of CDN, and can be fully customized to match the look and feel of your application.

Should you prefer to build your own components to integrate Spinwheel functionality, you can also consume our APIs directly. This provides a higher degree of control over the user experience and gives you full access to the robust liability data we have. The bulk of this documentation concerns the APIs themselves and is divided by endpoint as follows.

API SubDomain Description
Users / User objects contain all of the liability data belonging to your customer like individual loans details and a summary of their outstanding debt. They are created when a customer connects their loans via the Connect DIM.
Users /transactions Transaction objects are full transaction histories for a User that come from the loan servicers themselves and encapsulate all transactions, including those that occur outside the Spinwheel platform.
LoanServicers / LoanServicer objects represent the institutions that servicer your customers loans and can be accessed to display information like the institution's name and logo.
Payments /payers Payer objects represent the account that supplies the funds for payment. Different types of Payers can be created based on the use case that suits your needs the best, such as a User Payer that represents your customers own account, or a Partner Payer that represents an account belonging to your organization.
Payments /useOfFunds UseOfFunds objects represent where a payment goes and how it is applied. These are designed to provide maximal control, whether you'd like to specify a percentage split between individual loans or simply have us apply the payment the the liability where it will make the greatest impact.
Payments /requests Request objects represent the initiation of a payment. Request objects specify the User, the Payer, the UseOfFunds, the amount, and whether it's one-time or recurring. The creation of a Request will, in turn, produce at least one Transaction, but possibly more if multiple loan servicers are specified in the UseOfFunds object or if it's recurring.
Payments /transactions Transactions represent the actual flow of funds from a Payer account to an individual entity. They can only be created via a Request object and possess a life cycle to indicate the status of the transaction.

Authentication

Once you've created a Spinwheel account and received credentials, you can access all of Spinwheel's APIs using a server-to-server call (in order to protect your credentials). When calling an API simply include your secretKey as a bearer token in an auth header like so:

Authorization: Bearer 8ae8d6fa-df8d-4c5d-9f9b-c4e4c10968b0

Users

User objects are a robust representation of a customer and their student loan data. A User is created the first time a customer uses the Connect Drop-in Module to authenticate with their student loan servicer. Once authenticated, all of a customer's loans with that loan servicer will be available on the User object.

Due to the fact that students receive a new loan every term they're in school, student debt is one of the most complex debt categories that exists. Broadly speaking, most debt holders have a number of loans, those loans are grouped into loan accounts with a loan servicer. Customers can have loans with multiple loan servicers and should this be the case, they will be need to authenticate with each of their student loan servicers.

Once a customer has authenticated their accounts, the User object will have:

  1. StudentLoanAccountSummary: This object represents the customer's aggregate student debt profile and includes items such as total debt outstanding and a blended interest rate.

  2. StudentLoanAccounts: This array is made up of objects that represent each of the loan servicer accounts the customer has authenticated.

  3. LoanAccounts: Located within each StudentLoanAccount, this array contains the granular details of the underlying loans grouped by account.

  4. Profile: Also located within a StudentLoanAccount, this object holds customer information that has been verified by the student loan servicer.

Whenever a customer uses the Connect Drop-in, be it for the first time or the fortieth, a UserId will be returned in the response, which will be required when performing any action on the Spinwheel platform that involves a User.

Retrieve a User

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/users',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/users', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/users \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/users',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/users

This API is the workhorse that provides all the information you need about a customer's student debt. Simply pass the UserId that was returned from the Connect Drop-in as a query parameter to the users endpoint and you will recieve the corresponding User object in the response.

Parameters

Name In Type Required Description
userId query undefined false Spinwheel userId to identify a user.
extUserId query undefined false A client id registered with Spinwheel on user creation.
liabilityType[] query array[string] false The type of liability.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "_id": "b2a6cc17-cd7d-4c9a-9ae5-11a642f5e193",
    "extUserId": "testuser@gmail.com",
    "partnerId": "65dc5a58-ad06-4a0d-8467-b022f4e72925",
    "studentLoanAccountSummary": {
      "recommendedLoanId": "31d737a3-a78e-4eca-ae25-7a06b246534a",
      "numberOfLoanServicers": 1,
      "numberOfLoanAccounts": 1,
      "outstandingBalance": 18028.16,
      "outstandingInterest": 0,
      "regularMonthlyPayment": 213.37,
      "pendingLoanTermInMonths": 116,
      "interestRate": 4.875,
      "originalPrincipalAmount": 24980,
      "principalBalance": 18028.16
    },
    "studentLoanAccounts": [
      {
        "connectionStatus": {
          "isAuthSuccessful": true,
          "lastAuthAttemptOn": 1598509488988,
          "lastSuccessfulAuthOn": 1598509488988,
          "statusCode": 2000,
          "isValid": true,
          "isSecurityStepPending": false,
          "description": "Auth successful!",
          "userAction": null
        },
        "loanAccounts": [
          {
            "loans": [
              {
                "createdOn": 1598509488988,
                "loanName": "PERKINS-N - (PER24A)",
                "dueDate": "2020-10-10T00:00:00.000Z",
                "loanType": "unSubsidized",
                "status": "IN REPAYMENT",
                "loanStatusEndDate": "2020-10-10T00:00:00.000Z",
                "guarantor": "Dept. of Education",
                "origAmount": 4500,
                "interestRate": 5,
                "interestRateType": "FIXED",
                "interestRateDiscount": 0.25,
                "interestRateEffectiveDate": "2020-10-10T00:00:00.000Z",
                "principalBalance": 4009.05,
                "outstandingInterest": 0,
                "regularMonthlyPayment": 143.65,
                "repaymentPlan": "120",
                "outstandingBalance": 4009.05,
                "loanId": "4cdac9eb-079e-44d7-a9af-fdd62837a66d",
                "pendingLoanTermInMonths": 31,
                "calculatedPriorityScore": 805,
                "groupName": "A",
                "loanOriginationDate": "2020-09-11T00:00:00.000Z",
                "disbursementDates": [
                  "2020-09-12T00:00:00.000Z",
                  "2020-10-10T00:00:00.000Z"
                ],
                "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
                "lastPaymentDate": "2020-10-12T00:00:00.000Z",
                "lastPayment": 105,
                "isOverdue": true,
                "minimumPaymentAmount": 100,
                "ytdInterestPaid": 3037.17,
                "ytdPrincipalPaid": 3240.09,
                "isSubsidizedDerived": true,
                "loanSubsidyScheme": "Subsidized"
              },
              {
                "createdOn": 1598509488988,
                "loanName": "UNIVERSITY STUDENT LOAN - (USL01A)",
                "dueDate": "2020-10-10T00:00:00.000Z",
                "loanType": "unSubsidized",
                "status": "IN REPAYMENT",
                "loanStatusEndDate": "2020-10-10T00:00:00.000Z",
                "origAmount": 3000,
                "interestRate": 5,
                "principalBalance": 2709.71,
                "outstandingInterest": 0,
                "regularMonthlyPayment": 95.76,
                "repaymentPlan": "120",
                "outstandingBalance": 2709.71,
                "loanId": "8c723c5f-30fd-4bc9-916b-3af1db34dcd5",
                "pendingLoanTermInMonths": 32,
                "calculatedPriorityScore": 810,
                "groupName": "A",
                "loanOriginationDate": "2020-09-11T00:00:00.000Z",
                "disbursementDates": [
                  "2020-09-12T00:00:00.000Z",
                  "2020-10-10T00:00:00.000Z"
                ],
                "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
                "lastPaymentDate": "2020-10-12T00:00:00.000Z",
                "lastPayment": 321,
                "isOverdue": false,
                "minimumPaymentAmount": 240,
                "ytdInterestPaid": 1037.17,
                "ytdPrincipalPaid": 1140.09
              }
            ],
            "payments": [
              {
                "paymentDate": "2020-07-10T00:00:00.000Z",
                "paymentAmount": 239.41,
                "appliedPrincipal": 239.41,
                "appliedInterest": 0,
                "appliedFees": 0,
                "paymentType": "ACH"
              },
              {
                "paymentDate": "2020-07-06T00:00:00.000Z",
                "paymentAmount": 226.91,
                "appliedPrincipal": 226.91,
                "appliedInterest": 0,
                "appliedFees": 0,
                "paymentType": "CREDIT"
              }
            ],
            "summary": {
              "outstandingBalance": 6718.76,
              "dueDate": "2020-10-10T00:00:00.000Z",
              "amountDue": 239.41,
              "lastPayment": 239.41,
              "lastPaymentDate": "2020-10-10T00:00:00.000Z",
              "interestRate": 5,
              "regularMonthlyPayment": 239.41,
              "pendingLoanTermInMonths": 32,
              "originalPrincipalAmount": 7500,
              "principalBalance": 6718.76,
              "recommendedLoanId": "8c723c5f-30fd-4bc9-916b-3af1db34dcd5",
              "lastStatementIssueDate": "2020-10-10T00:00:00.000Z",
              "lastStatementBalance": 250.41,
              "paymentReferenceNumber": 1809402216743
            },
            "name": "heartLandAcc",
            "loanAccountId": "cb4274ac-95d4-46f3-8794-3e47fb645a2e"
          }
        ],
        "updatedOn": 1598509488988,
        "createdOn": 1598509488988,
        "profile": {
          "name": "Test user",
          "email": "testuser@gmail.com",
          "phoneNumber": "999999999",
          "zipCode": "99999"
        },
        "summary": {
          "outstandingBalance": 6718.76,
          "recommendedLoanId": "8c723c5f-30fd-4bc9-916b-3af1db34dcd5",
          "regularMonthlyPayment": 239.41,
          "pendingLoanTermInMonths": 32,
          "interestRate": 5,
          "originalPrincipalAmount": 7500,
          "principalBalance": 6718.76
        },
        "loanServicerId": "2ca12f1e-2632-40d9-9bcc-82cde093fdf9",
        "accountId": "fd760f07-710a-47cc-ad1e-a26240cc8ad2"
      },
      {
        "connectionStatus": {
          "isAuthSuccessful": true,
          "lastAuthAttemptOn": 1598509488988,
          "lastSuccessfulAuthOn": 1598509488988,
          "statusCode": 2000,
          "isValid": true,
          "isSecurityStepPending": false,
          "description": "Auth successful!",
          "userAction": null
        },
        "loanAccounts": [
          {
            "createdOn": 1597753517531,
            "loanName": "1",
            "dueDate": "2020-10-12T00:00:00.000Z",
            "status": "Covid 19 Pandemic Forbearance",
            "loanType": "subsidized",
            "origAmount": 5500,
            "interestRate": 4.45,
            "principalBalance": 5172.73,
            "groupName": "A",
            "outstandingInterest": 0,
            "regularMonthlyPayment": 56.99,
            "pendingLoanTermInMonths": 116,
            "repaymentPlan": "Standard Repayment",
            "outstandingBalance": 5172.73,
            "loanId": "3872301f-bf6a-467b-82b1-6d5af76d7c32",
            "calculatedPriorityScore": 545,
            "loanOriginationDate": "2016-06-12T00:00:00.000Z",
            "disbursementDates": [
              "2016-08-12T00:00:00.000Z",
              "2017-04-10T00:00:00.000Z"
            ],
            "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
            "lastPaymentDate": "2020-10-12T00:00:00.000Z",
            "lastPayment": 321,
            "isOverdue": false,
            "minimumPaymentAmount": 240.5,
            "ytdInterestPaid": 1037.17,
            "ytdPrincipalPaid": 1140.09,
            "isSubsidizedDerived": true,
            "loanSubsidyScheme": "Subsidized"
          },
          {
            "createdOn": 1597753517532,
            "loanName": "2",
            "dueDate": "2020-10-12T00:00:00.000Z",
            "status": "Covid 19 Pandemic Forbearance",
            "loanType": "unSubsidized",
            "origAmount": 7000,
            "interestRate": 4.45,
            "principalBalance": 1224.49,
            "groupName": "B",
            "outstandingInterest": 0,
            "regularMonthlyPayment": 19.36,
            "pendingLoanTermInMonths": 116,
            "repaymentPlan": "Standard Repayment",
            "outstandingBalance": 1224.49,
            "loanId": "5661d1c0-2a26-4227-9a10-81a65a7dacc3",
            "calculatedPriorityScore": 755,
            "loanOriginationDate": "2017-07-13T00:00:00.000Z",
            "disbursementDates": [
              "2017-09-12T00:00:00.000Z",
              "2018-05-10T00:00:00.000Z"
            ],
            "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
            "lastPaymentDate": "2020-10-12T00:00:00.000Z",
            "lastPayment": 321,
            "isOverdue": false,
            "minimumPaymentAmount": 252.19,
            "ytdInterestPaid": 1037.17,
            "ytdPrincipalPaid": 1140.09
          },
          {
            "createdOn": 1597753517532,
            "loanName": "3",
            "dueDate": "2020-10-12T00:00:00.000Z",
            "status": "Covid 19 Pandemic Forbearance",
            "loanType": "subsidized",
            "origAmount": 5500,
            "interestRate": 5.05,
            "principalBalance": 4420.28,
            "groupName": "C",
            "outstandingInterest": 0,
            "regularMonthlyPayment": 58.61,
            "pendingLoanTermInMonths": 116,
            "repaymentPlan": "Standard Repayment",
            "outstandingBalance": 4420.28,
            "loanId": "44cd1704-176c-47b7-ba86-d0780120d26a",
            "calculatedPriorityScore": 610,
            "loanOriginationDate": "2017-12-12T00:00:00.000Z",
            "disbursementDates": [
              "2018-02-12T00:00:00.000Z",
              "2018-108-10T00:00:00.000Z"
            ],
            "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
            "lastPaymentDate": "2020-10-12T00:00:00.000Z",
            "lastPayment": 321,
            "isOverdue": false,
            "minimumPaymentAmount": 240,
            "ytdInterestPaid": 1037.17,
            "ytdPrincipalPaid": 1140.09
          },
          {
            "createdOn": 1597753517532,
            "loanName": "4",
            "dueDate": "2020-10-12T00:00:00.000Z",
            "status": "Covid 19 Pandemic Forbearance",
            "loanType": "unSubsidized",
            "origAmount": 6980,
            "interestRate": 5.05,
            "principalBalance": 7210.66,
            "groupName": "D",
            "outstandingInterest": 0,
            "regularMonthlyPayment": 78.41,
            "pendingLoanTermInMonths": 116,
            "repaymentPlan": "Standard Repayment",
            "outstandingBalance": 7210.66,
            "loanId": "31d737a3-a78e-4eca-ae25-7a06b246534a",
            "calculatedPriorityScore": 805,
            "loanOriginationDate": "2018-08-15T00:00:00.000Z",
            "disbursementDates": [
              "2018-10-12T00:00:00.000Z",
              "2019-02-10T00:00:00.000Z"
            ],
            "expectedPayoffDate": "2030-01-10T00:00:00.000Z",
            "lastPaymentDate": "2020-10-12T00:00:00.000Z",
            "lastPayment": 321,
            "isOverdue": false,
            "minimumPaymentAmount": 200,
            "ytdInterestPaid": 1037.17,
            "ytdPrincipalPaid": 1140.09
          }
        ],
        "payments": [
          {
            "paymentDate": "2020-08-14T00:00:00.000Z",
            "paymentAmount": 300,
            "appliedPrincipal": 300,
            "appliedInterest": 0,
            "appliedFees": 0,
            "paymentType": "Electronic"
          },
          {
            "paymentDate": "2020-08-10T00:00:00.000Z",
            "paymentAmount": 1,
            "appliedPrincipal": 1,
            "appliedInterest": 0,
            "appliedFees": 0,
            "paymentType": "Electronic"
          },
          {
            "paymentDate": "2020-08-07T00:00:00.000Z",
            "paymentAmount": 2.71,
            "appliedPrincipal": 2.71,
            "appliedInterest": 0,
            "appliedFees": 0,
            "paymentType": "Electronic"
          },
          {
            "paymentDate": "2020-08-04T00:00:00.000Z",
            "paymentAmount": 0.02,
            "appliedPrincipal": 0.02,
            "appliedInterest": 0,
            "appliedFees": 0,
            "paymentType": "Electronic"
          }
        ],
        "name": "gsmrAcc1",
        "summary": {
          "outstandingBalance": 18028.16,
          "dueDate": "2020-10-12T00:00:00.000Z",
          "pastDue": 0,
          "amountDue": 0,
          "status": "Covid 19 Pandemic Forbearance",
          "lastPayment": 300,
          "outstandingInterest": 0,
          "interestRate": 4.875,
          "regularMonthlyPayment": 213.37,
          "pendingLoanTermInMonths": 116,
          "originalPrincipalAmount": 24980,
          "principalBalance": 18028.16,
          "recommendedLoanId": "31d737a3-a78e-4eca-ae25-7a06b246534a",
          "lastStatementIssueDate": "2021-01-10T00:00:00.000Z",
          "lastStatementBalance": 213.37,
          "paymentReferenceNumber": 2009402210980
        },
        "loanAccountId": "4a6a3b15-7468-4629-b964-02c528bcb035"
      }
    ],
    "createdOn": 1597317116353,
    "updatedOn": 1597753517524,
    "isBorrower": true
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}
{
  "status": {
    "code": 400,
    "desc": "Account not found"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful UserInfoSuccessResponse
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Retrieve a User's Transactions

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/users/{userId}/transactions/',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/users/{userId}/transactions/', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/users/{userId}/transactions/ \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/users/{userId}/transactions/',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/users/{userId}/transactions/

The User API contains its own Transactions object that is distinct from the Transactions object on the Payments API. Unlike that object, the User Transactions object represents all the transactions that are known to the loan servicer, whether they occurred through the Spinwheel platform or not. In addition to the split between principal and interest, the object's properties include the loan servicer, the loan account, and the payment type amongst others. The base API call involves passing the userId as a slug to the above URL, though there are a number of available filters as shown in the schema below.

Parameters

Name In Type Required Description
userId path undefined false Spinwheel userId to identify a user.
liabilityType[] query array[string] false The type of liability.
showPlatformPayments query undefined false This filter is used to fetch only the transactions done from the platform.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
      "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
      "paymentDate": "2021-01-03T18:30:00.000Z",
      "paymentAmount": 400,
      "appliedPrincipal": 222.25,
      "appliedInterest": 177.75,
      "appliedFees": 0,
      "paymentType": "Regular Payment",
      "paymentDetails": [
        {
          "appliedTxnId": "c2cbd78-8573-4e18-890c-f83a29e4c4b7",
          "appliedToLoans": [
            {
              "loanId": "c2334d78-8573-4e18-890c-f83a29e4c4b7",
              "percentage": 100
            }
          ]
        }
      ],
      "isInternalTxn": true,
      "status": "APPLIED",
      "loanServicerName": "Firstmark Services"
    },
    {
      "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
      "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
      "paymentDate": "2021-03-01T13:46:47.165Z",
      "paymentAmount": 3.5,
      "isInternalTxn": true,
      "status": "SCHEDULED"
    },
    {
      "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
      "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
      "paymentDate": "2021-03-01T13:46:47.165Z",
      "paymentAmount": 6.5,
      "isInternalTxn": true,
      "status": "SCHEDULED",
      "loanServicerName": "Firstmark Services"
    }
  ]
}

Invalid request. (Failed to validate one or more properties of request sent)

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful TxnHistorySuccessResponse
400 Bad Request Invalid request. (Failed to validate one or more properties of request sent) StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Loan Servicers

LoanServicer objects represent the institutions that service your customer's student loans. A LoanServicer's Id corresponds to the the Id listed on a studentLoanAccount in the User object, so that you can use these APIs to provide your customer with pertinent details about their loan servicer such as the institution's name and logo.

Get a List of All Supported Loan Servicers

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/md/loanServicers',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/md/loanServicers', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/md/loanServicers \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/md/loanServicers',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/md/loanServicers

Returns a list of all the loan servicers currently supported by Spinwheel with the attendant loanServicerIds.

Parameters

Name In Type Required Description
name query undefined false Optional name query to filter the supported loan servicers by name.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "isIntegrated": true,
      "_id": "097ac8fa-6b11-49c6-8889-6dec25f83f55",
      "forgotPasswordUrl": "https://accountaccess.myfedloan.org/authentication/cantAccess.html",
      "logoUrl": "https://cdn.spinwheel.io/images/loanservicers/logo_fedloan-servicing.svg",
      "loginUrl": "https://myfedloan.org",
      "name": "FedLoan Servicing"
    },
    {
      "isIntegrated": true,
      "_id": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
      "forgotPasswordUrl": "https://secure.firstmarkservices.com/Account/ForgotPassword",
      "logoUrl": "https://cdn.spinwheel.io/images/loanservicers/logo_firstmark.svg",
      "loginUrl": "https://secure.firstmarkservices.com/Account/Login",
      "name": "Firstmark Services"
    }
  ]
}

Unauthorized

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid auth key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetLoanServicersResponseModel
401 Unauthorized Unauthorized StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Retrieve a Single Loan Servicer by ID

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/md/loanServicers/{loanServicerId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/md/loanServicers/{loanServicerId}', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/md/loanServicers/{loanServicerId} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/md/loanServicers/{loanServicerId}',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/md/loanServicers/{loanServicerId}

Retrieves the details of a single loanServicer object on success.

Parameters

Name In Type Required Description
loanServicerId path undefined true id of the loan servicer

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "isIntegrated": true,
    "_id": "097ac8fa-6b11-49c6-8889-6dec25f83f55",
    "forgotPasswordUrl": "https://accountaccess.myfedloan.org/authentication/cantAccess.html",
    "logoUrl": "https://cdn.spinwheel.io/images/loanservicers/logo_fedloan-servicing.svg",
    "loginUrl": "https://myfedloan.org",
    "name": "FedLoan Servicing"
  }
}

Invalid request

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}
{
  "status": {
    "code": 400,
    "desc": "Loan Servicer with ID: 097ac8fa-6b11-49c6-8889-6dec25f83f5e not found!"
  }
}

Unauthorized

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid auth key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetLoanServicerByIdResponseModel
400 Bad Request Invalid request StatusResponseModel
401 Unauthorized Unauthorized StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Payments - Payers

Payments begin with the account that serves as the source of funds for the payment. On the Spinwheel platform, this account is represented by a Payer object.

There are three types of Payers you can configure, depending on your needs:

  1. User Payer - This type of Payer represents a customer's account. This is a great option when customers maintain accounts on your platform or you have ready access to your customer's account information. A typical use case for this type of Payer is when you'd like to enable a customer to passively contribute to their loans by rounding up purchases they make with a card or account belonging to your organization.

  2. Partner Payer - This type of Payer represents an account belonging to your organization and allows you to directly contribute to your customer's loans on their behalf. This option works well, for instance, to enable your customers to redeem your organization's loyalty points and pay down their loans.

  3. Platform Payer - Should you want to use your organization's funds as the source of funds, but would prefer not to pull directly from your organization's acount, we can work with you to set up an FBO account. This configuration cannot be done programmatically, so if this is your preferred setup, please reach out to a Spinwheel sales representative here.

Whichever option suits you best, when you successfully create a Payer object, the response will contain a PayerId. That PayerId is what you'll use to specify the funding source whenever you create a Transaction.

Create a User Payer

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/v1/payments/payers/user',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/v1/payments/payers/user', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/v1/payments/payers/user \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "userId": "8772831c-2a22-4b00-bcae-825c6aed93ed",
  "bankAccountDetails": {
    "routingNumber": "2389048123",
    "accountNumber": "9110001011010",
    "nameOnAccount": "Test User",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "accountType": "CHECKING",
    "accountHolderProfile": {
      "country": "USA",
      "city": "Chicago",
      "zipCode": "60601",
      "email": "testuser@test.com",
      "phone": "9999999999",
      "addressLine1": "Opp. Willis Tower, 233 S Wacker Dr"
    }
  }
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/payers/user',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /v1/payments/payers/user

To create a Payer object for a User, you will send a POST request with a request body represented in the sample below*. Since, just like in the real world, a customer may have a number of accounts from which to make payments, Users can have multiple Payers. The PayerId that is returned with a successful API call can be used to specify the source of funds when making a payment.

*NOTE: all fields within bankAccountDetails object are mandatory because they are required by the loan servicers themselves

Body parameter

{
  "userId": "8772831c-2a22-4b00-bcae-825c6aed93ed",
  "bankAccountDetails": {
    "routingNumber": "2389048123",
    "accountNumber": "9110001011010",
    "nameOnAccount": "Test User",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "accountType": "CHECKING",
    "accountHolderProfile": {
      "country": "USA",
      "city": "Chicago",
      "zipCode": "60601",
      "email": "testuser@test.com",
      "phone": "9999999999",
      "addressLine1": "Opp. Willis Tower, 233 S Wacker Dr"
    }
  }
}

Parameters

Name In Type Required Description
body body CreatePayerUserModel true Post bank account details of a user.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "payerId": "c555734f-6473-4d13-9943-2264cb1ee2dd",
    "userId": "8772831c-2a22-4b00-bcae-825c6aed93ed",
    "payerType": "USER",
    "accountLast4Digits": "1010",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful PayerResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Create a Partner Payer

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/v1/payments/payers/partner',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/v1/payments/payers/partner', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/v1/payments/payers/partner \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "bankAccountDetails": {
    "routingNumber": "2389048123",
    "accountNumber": "9110001011010",
    "nameOnAccount": "Test User",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "accountType": "CHECKING",
    "accountHolderProfile": {
      "country": "USA",
      "city": "Chicago",
      "zipCode": "60601",
      "email": "testuser@test.com",
      "phone": "9999999999",
      "addressLine1": "Opp. Willis Tower, 233 S Wacker Dr"
    }
  }
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/payers/partner',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /v1/payments/payers/partner

To create a Payer object that belongs to your organization, you will send a POST request with a request body represented in the sample below*. You are NOT limited to one Partner Payer, so feel free to create as many Payers as needed. The PayerId that is returned with a successful API call can be used to specify the source of funds when making a payment.

*NOTE: all fields within bankAccountDetails object are mandatory because they are required by the loan servicers themselves

Body parameter

{
  "bankAccountDetails": {
    "routingNumber": "2389048123",
    "accountNumber": "9110001011010",
    "nameOnAccount": "Test User",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "accountType": "CHECKING",
    "accountHolderProfile": {
      "country": "USA",
      "city": "Chicago",
      "zipCode": "60601",
      "email": "testuser@test.com",
      "phone": "9999999999",
      "addressLine1": "Opp. Willis Tower, 233 S Wacker Dr"
    }
  }
}

Parameters

Name In Type Required Description
body body CreatePayerPartnerModel true Post bank account details of a user.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "payerId": "c555734f-6473-4d13-9943-2264cb1ee2dd",
    "payerType": "PARTNER",
    "accountLast4Digits": "1010",
    "institutionName": "Axis Bank Pvt. Ltd.",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful PayerResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Get a List of Payers

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/payers',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/payers', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/payers \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/payers',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/payers

In cases where you need to retrieve a list of Payers, you can do it using query parameters in one of two ways depending on the type of Payer.

  1. To retrieve the Payers that belong to a User, you will add the userId of the User to the above URL. If you wanted to get the Payers for a user with a userId of '971de944-a5f6-42e2-bdc2-af13313d1fae' you'd send a GET request to: /v1/payments/payers?userId=971de944-a5f6-42e2-bdc2-af13313d1fae

  2. If you've elected to configure either Platform Payers or Partner Payers, this API may be less useful to you since the Payer is less likely to change by transaction. Nonetheless, you can retrieve a list of the Payers you've created by sending a GET request like so: /v1/payments/payers?payerType='PARTNER'

In either case, a successful response returns an array that contains identifying information for each of the Payers, including the PayerId, the name of the institution, and the last four digits of the account number.

Parameters

Name In Type Required Description
userId query undefined false Spinwheel userId.
payerType query string false Type of the payer.

Enumerated Values

Parameter Value
payerType PARTNER
payerType PLATFORM

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "payerId": "c555734f-6473-4d13-9943-2264cb1ee2dd",
      "userId": "ab2734f-6473-4d13-9943-2264cb1ee2dd",
      "payerType": "USER",
      "accountLast4Digits": "1010",
      "institutionName": "Axis Bank Pvt. Ltd.",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    },
    {
      "payerId": "d4a5734f-6473-4d13-9943-2264cb1ee2dd",
      "userId": "8be734f-6473-4d13-9943-2264cb1ee2dd",
      "payerType": "USER",
      "accountLast4Digits": "1012",
      "institutionName": "HDFC Bank Pvt. Ltd.",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    },
    {
      "payerId": "6ce5734f-6473-4d13-9943-2264cb1ee2dd",
      "userId": "8be734f-6473-4d13-9943-2264cb1ee2dd",
      "payerType": "USER",
      "accountLast4Digits": "5453",
      "institutionName": "JPMorgan Chase & Co.",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    }
  ]
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "payerId": "6ce5734f-6473-4d13-9943-2264cb1ee2dd",
      "payerType": "PARTNER",
      "accountLast4Digits": "1111",
      "institutionName": "Bank of America Corp.",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    }
  ]
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "payerId": "6ce5734f-6473-4d13-9943-2264cb1ee2dd",
      "payerType": "PLATFORM",
      "accountLast4Digits": "1212",
      "institutionName": "Citigroup Inc.",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    }
  ]
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetPayersResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Delete a Payer

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.delete 'http://localhost:8080/v1/payments/payers/{payerId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.delete('http://localhost:8080/v1/payments/payers/{payerId}', headers = headers)

print(r.json())

# You can also use wget
curl -X DELETE http://localhost:8080/v1/payments/payers/{payerId} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/payers/{payerId}',
{
  method: 'DELETE',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

DELETE /v1/payments/payers/{payerId}

Should you or your customers want to remove a Payer, you can do so using this API. In your DELETE request, simply add the PayerId as a slug. Please note that when a Payer is deleted, we will keep a reference to it in order to maintain the integrity of the transaction history. A success response will contain a confirmation message with a reference to the deleted payer account.

Parameters

Name In Type Required Description
payerId path undefined true payerId that you received on creating a payer

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "Payer 1652af32-d375-405f-abb5-865cf7f05c7c deleted successfully!"
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful StatusResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Payments - Use Of Funds

If the Payer object represents where a payment comes from, the UseOfFunds object represents where the funds will go and how they will be applied.

The three properties at your disposal are highImpact, applyToStatement, and allocation.

*Note: There are a few loan servicers that, because of the way their systems are built, do not accept payments to individual loans. Should you attempt to use a loan-level allocation with these servicers, the loan-level splits will be ignored but any account-level splits will be applied.

Now that you understand how to configure a UseOfFunds object, let's talk about how it works in the context of a payment. You have two options, broadly, when specifying the UseOfFunds: 1. You can create a UseOfFunds object and pass the UseOfFundsId that was returned on creation like so: useOfFundsId: 5f97bf45-2b74-479a-b9ba-1e64f521e99b, or 2. You can pass the same request body that you'd use to create a UseOfFunds object, directly on a useOfFunds property.

Create a UseOfFunds Object

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/v1/payments/useOfFunds',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/v1/payments/useOfFunds', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/v1/payments/useOfFunds \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
  "highImpact": true,
  "applyTowardsStatement": false
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/useOfFunds',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /v1/payments/useOfFunds

This API will allow you to create a UseOfFunds object that you can then pass a reference to when creating a Request to make a payment on a Users student loans. A successful call will return a response that contains the UseOfFundsId.

Body parameter

{
  "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
  "highImpact": true,
  "applyTowardsStatement": false
}

Parameters

Name In Type Required Description
body body CreateUseOfFundsModel true Create useOfFunds for a user which can be used for making payments.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "useOfFundsId": "392683d5-108d-4610-94cf-88bbb46990c9",
    "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
    "applyTowardsStatement": false,
    "highImpact": true,
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "useOfFundsId": "392683d5-108d-4610-94cf-88bbb46990c9",
    "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
    "applyTowardsStatement": false,
    "allocation": [
      {
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
        "percentage": 50,
        "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
        "loans": [
          {
            "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
            "percentage": 20
          },
          {
            "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
            "percentage": 30
          },
          {
            "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
            "percentage": 50
          }
        ]
      },
      {
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
        "percentage": 50,
        "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
        "loans": [
          {
            "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
            "percentage": 100
          }
        ]
      }
    ],
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful UseOfFundsResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Get a List of a User's UseOfFunds Objects

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/useOfFunds',
  params: {
  'userId' => 'undefined'
}, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/useOfFunds', params={
  'userId': undefined
}, headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/useOfFunds \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/useOfFunds',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/useOfFunds

Should you want to get a list of the UseOfFunds objects associated with a given User, simply pass the UserId as a query paramter in a GET request.

Parameters

Name In Type Required Description
userId query undefined true userId of the user you added useOfFunds allocation to.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "useOfFundsId": "392683d5-108d-4610-94cf-88bbb46990c9",
      "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
      "applyTowardsStatement": false,
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 20
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 30
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 50
            }
          ]
        },
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 100
            }
          ]
        }
      ],
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    },
    {
      "useOfFundsId": "a75fdfcd-cac4-4186-8671-6e4350cf2fba",
      "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
      "highImpact": true,
      "applyTowardsStatement": false,
      "createdOn": 1614603026493,
      "updatedOn": 1614603026493
    }
  ]
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetUseOfFundsResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Get the Details of a Single UseOfFunds Object

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/useOfFunds/{useOfFundsId}

To retrieve the details of a single UseOfFunds object, simply make a GET request with the UseOfFundsId as a slug to the above URL.

Parameters

Name In Type Required Description
useOfFundsId path undefined true useOfFundsId that you received on creating a useOfFunds

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "useOfFundsId": "392683d5-108d-4610-94cf-88bbb46990c9",
    "userId": "bd067a4c-260a-4fa0-8fa9-6c7d38aba1d7",
    "applyTowardsStatement": false,
    "highImpact": true,
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful UseOfFundsResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Delete a UseOfFunds Object

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json'
}

result = RestClient.delete 'http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json'
}

r = requests.delete('http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}', headers = headers)

print(r.json())

# You can also use wget
curl -X DELETE http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId} \
  -H 'Accept: application/json'


const headers = {
  'Accept':'application/json'
};

fetch('http://localhost:8080/v1/payments/useOfFunds/{useOfFundsId}',
{
  method: 'DELETE',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

DELETE /v1/payments/useOfFunds/{useOfFundsId}

You can DELETE a UseOfFunds object should you so choose using this API. It should be noted that to preserve the integrity of the transaction history amongst other things, this is a 'soft' DELETE so that references to it are preserved.

Parameters

Name In Type Required Description
useOfFundsId path undefined true useOfFundsId that you received on creating a useOfFunds

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "useOfFunds 1652af32-d375-405f-abb5-865cf7f05c7c deleted successfully!"
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful StatusResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Payments - Requests

Request objects represent the initiation of one or more payments. Requests require:

At a high level, there are two types of Requests: 'ONE_TIME' and 'RECURRING'. Whenever a valid Request object is created, it will in turn create at least one Transaction object. To better understand the relationship between Requests and Transactions, it's helpful to walk through a few use cases.

  1. The simplest example would be a oneTime Request on behalf of a User that has only one loan with a single loan servicer. When the request is created, Spinwheel will schedule a payment with that loan servicer, who will then attempt to draw the funds from the account represented by the specified Payer. That payment is represented by a Transaction object, which has a status property to reflect which stage of the Transaction lifecycle it's in.
  2. One level of complexity up from that example is a oneTime Request on a behalf of a User with two loans at two separate loan servicers. This time, Spinwheel will schedule a payment with each of the loan servicers and create two Transaction objects to represent them.
  3. Finally, the same request from example 2 could be made as a recurring Request. Recurring Requests require a 'schedule' property that outlines when, during the month, Spinwheel should schedule payments with the loan servicers and create the Transaction objects that represent them.

As you can see, the above examples are relatively straightforward but it's easy to imagine many more degrees of complexity depending on how many loans and loan servicers a User has. The best way to think about Requests is as Transaction factories.

Now that we understand the concept, let's go through a some of the properties in greater detail:

Create a Request

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/v1/payments/requests',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/v1/payments/requests', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/v1/payments/requests \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
  "amount": 100,
  "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
  "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
  "useOfFunds": {
    "highImpact": true,
    "applyTowardsStatement": false
  },
  "schedule": {
    "endDate": "2021-03-01",
    "startDate": "2023-03-01",
    "dayOfTheMonth": 2,
    "isLastDayOfMonth": true
  },
  "requestType": "RECURRING"
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/requests',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /v1/payments/requests

A POST request to this endpoint will create a new payment Request object. When that Request has been successfully created, the response will contain details of the underlying Transactions objects that were created as well as a Spinwheel generated requestId. Should you want to retrieve the details of a single Request, you'll use that requestId to do so.

Body parameter

{
  "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
  "amount": 100,
  "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
  "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
  "useOfFunds": {
    "highImpact": true,
    "applyTowardsStatement": false
  },
  "schedule": {
    "endDate": "2021-03-01",
    "startDate": "2023-03-01",
    "dayOfTheMonth": 2,
    "isLastDayOfMonth": true
  },
  "requestType": "RECURRING"
}

Parameters

Name In Type Required Description
body body CreateRequestModel true Create a payment request by providing schedule, useOfFunds, and payer information.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "highImpact": true,
      "applyTowardsStatement": false
    },
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 20
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 30
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 50
            }
          ]
        },
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 100
            }
          ]
        }
      ],
      "applyTowardsStatement": false
    },
    "scheduleTs": 1636250123000,
    "requestType": "ONE_TIME",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 20
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 30
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 50
            }
          ]
        },
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 100
            }
          ]
        }
      ],
      "applyTowardsStatement": false
    },
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFundsId": "a437c6e8-c87b-415e-94e2-15367975218d",
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful PaymentRequestResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Get a List of Requests

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/requests',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/requests', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/requests \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/requests',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/requests

To retrieve a list of Request objects, send a GET request to this endpoint with whatever query filters you'd like applied to that list. This should be fairly straightforward, but for clarity's sake, the three available query filters are userId for retrieving all Requests for a given User, extRequestId for getting a single request using the id you created, and requestType which must be passed along with a userId.

Parameters

Name In Type Required Description
userId query undefined false Spinwheel userId
extRequestId query undefined false extRequestId passed by you in the payments request
requestId query undefined false Spinwheel generated requestId of the payment request.
requestType query string false Filter on requestType

Enumerated Values

Parameter Value
requestType ONE_TIME
requestType RECURRING

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
      "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
      "amount": 100,
      "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
      "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
      "useOfFunds": {
        "allocation": [
          {
            "liabilityType": "STUDENT_LOAN",
            "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
            "percentage": 50,
            "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
            "loans": [
              {
                "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
                "percentage": 20
              },
              {
                "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
                "percentage": 30
              },
              {
                "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
                "percentage": 50
              }
            ]
          },
          {
            "liabilityType": "STUDENT_LOAN",
            "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
            "percentage": 50,
            "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
            "loans": [
              {
                "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
                "percentage": 100
              }
            ]
          }
        ],
        "applyTowardsStatement": false
      },
      "schedule": {
        "endDate": "2021-03-01",
        "startDate": "2023-03-01",
        "dayOfTheMonth": 2,
        "isLastDayOfMonth": true
      },
      "requestType": "RECURRING",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    },
    {
      "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
      "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
      "amount": 100,
      "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
      "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
      "useOfFundsId": "a437c6e8-c87b-415e-94e2-15367975218d",
      "schedule": {
        "endDate": "2021-03-01",
        "startDate": "2023-03-01",
        "dayOfTheMonth": 2,
        "isLastDayOfMonth": true
      },
      "requestType": "RECURRING",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    },
    {
      "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
      "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
      "amount": 100,
      "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
      "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
      "useOfFunds": {
        "highImpact": true,
        "applyTowardsStatement": false
      },
      "schedule": {
        "endDate": "2021-03-01",
        "startDate": "2023-03-01",
        "dayOfTheMonth": 2,
        "isLastDayOfMonth": true
      },
      "requestType": "RECURRING",
      "createdOn": 1614602485926,
      "updatedOn": 1614602485926
    }
  ]
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetPaymentRequestsResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Get the Details of a Single Request

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/requests/{requestId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/requests/{requestId}', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/requests/{requestId} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/requests/{requestId}',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/requests/{requestId}

To retrieve the details of a single Request, send a GET request to this endpoint with the Spinwheel requestId that you received when the Request was created as a slug.

Parameters

Name In Type Required Description
requestId path undefined false Spinwheel generated requestId of the payment request.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 20
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 30
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 50
            }
          ]
        },
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 100
            }
          ]
        }
      ],
      "applyTowardsStatement": false
    },
    "scheduleTs": 1636250123000,
    "requestType": "ONE_TIME",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 20
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 30
            },
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 50
            }
          ]
        },
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "e32be248-d43f-4dd4-9e34-05c7b8ab1888",
          "percentage": 50,
          "loanAccountId": "16c7a702-acd6-4177-9f1e-3d5a557934a5",
          "loans": [
            {
              "loanId": "75d37a78-6f07-4337-bbff-dfe772d1942e",
              "percentage": 100
            }
          ]
        }
      ],
      "applyTowardsStatement": false
    },
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFundsId": "a437c6e8-c87b-415e-94e2-15367975218d",
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}
{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "amount": 100,
    "payerId": "12b9d6e8-c87b-415e-94e2-15367975218d",
    "userId": "ac89d6e8-c87b-415e-94e2-15367975218d",
    "useOfFunds": {
      "highImpact": true,
      "applyTowardsStatement": false
    },
    "schedule": {
      "endDate": "2021-03-01",
      "startDate": "2023-03-01",
      "dayOfTheMonth": 2,
      "isLastDayOfMonth": true
    },
    "requestType": "RECURRING",
    "createdOn": 1614602485926,
    "updatedOn": 1614602485926
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful PaymentRequestResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Delete a Request

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json'
}

result = RestClient.delete 'http://localhost:8080/v1/payments/requests/{requestId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json'
}

r = requests.delete('http://localhost:8080/v1/payments/requests/{requestId}', headers = headers)

print(r.json())

# You can also use wget
curl -X DELETE http://localhost:8080/v1/payments/requests/{requestId} \
  -H 'Accept: application/json'


const headers = {
  'Accept':'application/json'
};

fetch('http://localhost:8080/v1/payments/requests/{requestId}',
{
  method: 'DELETE',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

DELETE /v1/payments/requests/{requestId}

Use this API to DELETE a Request by passing the requestId as a slug. When you DELETE a Request, no further Transactions will be created from that Request. It should be noted that to preserve the integrity of the transaction history amongst other things, this is a 'soft' DELETE so that references to it are preserved.

Parameters

Name In Type Required Description
requestId path undefined true requestId that you received on creating the payment request

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "Payment request with requestId: 1652af32-d375-405f-abb5-865cf7f05c7c, Deleted successfully!"
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful StatusResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Payments - Transactions

We were introduced to the concept of Transactions in our discussion of Requests, but let's get into greater detail here. A Transaction object represents a single payment to a loan servicer. As can be seen in the example responses, Transaction objects contain all the information you would expect like amount, a transactionId, and payerId amongst others. The Transaction property that is most useful and bears explaining is the 'status' property. Status describes where in the Transaction lifecycle a given Transaction object is. There are four statuses in a Transaction lifecycle:

  1. SCHEDULING: When a Transaction has been received by Spinwheel but not is yet in the process of being scheduled.
  2. PROCESSING: When a Transaction is in the process of being scheduled with the loan servicer by Spinwheel
  3. SCHEDULED: Once a Transaction has been successfully scheduled with a loan servicer
  4. FAILED: When a Transaction was not able to be successfully scheduled with a loan servicer

Get a List of Transactions

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/transactions',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/transactions', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/transactions \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/transactions',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/transactions

This API enables you to retrieve a list of Transactions using query filters. Similar to Requests, you can get a list of Transactions that associated with a given User or Request.

Parameters

Name In Type Required Description
userId query undefined false userId of the user towards whom payment was scheduled
requestId query undefined false requestId received on creating the payment request
extRequestId query undefined false extRequestId sent on creating a payment request

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": [
    {
      "transactionId": "a3857954-4b75-46ed-a358-b7c0883be777",
      "amount": 50,
      "status": "SCHEDULED",
      "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
      "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
      "payerId": "12b5c8d3-56bb-4b8e-9405-cf01d6c8a160",
      "userId": "5ca5c8d3-56bb-4b8e-9405-cf01d6c8a160",
      "useOfFunds": {
        "percentage": 50,
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "72a68490-c2e4-4a71-b65f-82b250c71abb",
        "loanAccountId": "72761b9b-f9f2-42f8-a703-753154b78203",
        "loans": [
          {
            "loanId": "62b297bf-6175-40d0-8c9b-27b85cd35f70",
            "percentage": 100
          }
        ],
        "applyTowardsStatement": false
      },
      "scheduleTs": 1614549451000,
      "createdOn": 1614549451000,
      "updatedOn": 1614549451000
    },
    {
      "transactionId": "12b57954-4b75-46ed-a358-b7c0883be777",
      "amount": 50,
      "status": "APPLIED",
      "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
      "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
      "payerId": "12b5c8d3-56bb-4b8e-9405-cf01d6c8a160",
      "userId": "5ca5c8d3-56bb-4b8e-9405-cf01d6c8a160",
      "useOfFunds": {
        "percentage": 50,
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "12a68490-c2e4-4a71-b65f-82b250c71abb",
        "loanAccountId": "ad761b9b-f9f2-42f8-a703-753154b78203",
        "loans": [
          {
            "loanId": "62b297bf-6175-40d0-8c9b-27b85cd35f70",
            "percentage": 100
          }
        ],
        "applyTowardsStatement": false
      },
      "scheduleTs": 1614549451000,
      "createdOn": 1614549451000,
      "updatedOn": 1614549451000
    }
  ]
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetTransactionsResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Retrieve the Details of a Single Transaction

Code samples

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.get 'http://localhost:8080/v1/payments/transactions/{transactionId}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.get('http://localhost:8080/v1/payments/transactions/{transactionId}', headers = headers)

print(r.json())

# You can also use wget
curl -X GET http://localhost:8080/v1/payments/transactions/{transactionId} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/payments/transactions/{transactionId}',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

GET /v1/payments/transactions/{transactionId}

To retrieve the details of a single Transaction, send a GET request to this endpoint with the transactionId that was returned when the Transaction was created by a Request.

Parameters

Name In Type Required Description
transactionId path undefined true transactionId that you received on fetching transaction details using requestId or transactionRequestId.

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "transactionId": "a3857954-4b75-46ed-a358-b7c0883be777",
    "amount": 50,
    "status": "SCHEDULED",
    "requestId": "fd97c6e8-c87b-415e-94e2-15367975218d",
    "extRequestId": "12c9d6e8-c87b-415e-94e2-15367975218d",
    "payerId": "12b5c8d3-56bb-4b8e-9405-cf01d6c8a160",
    "userId": "5ca5c8d3-56bb-4b8e-9405-cf01d6c8a160",
    "useOfFunds": {
      "percentage": 50,
      "liabilityType": "STUDENT_LOAN",
      "loanServicerId": "72a68490-c2e4-4a71-b65f-82b250c71abb",
      "loanAccountId": "72761b9b-f9f2-42f8-a703-753154b78203",
      "loans": [
        {
          "loanId": "62b297bf-6175-40d0-8c9b-27b85cd35f70",
          "percentage": 100
        }
      ],
      "applyTowardsStatement": false
    },
    "scheduleTs": 1614549451000,
    "createdOn": 1614549451000,
    "updatedOn": 1614549451000
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "No auth key provided"
  }
}
{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful GetTransactionByIdResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Drop-in Module

Using this API, you can retrieve either a Magic Link to directly invoke a DIM or you can retrieve a DIM token to invoke a DIM by means of CDN.

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/auth',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/auth', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/auth \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "module": "loan-servicers-login",
  "redirectUri": "https://127.0.0.1:4200/extract-token",
  "extUserId": "158a3456-5eda-429c-8759-b20f9493d841"
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/auth',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /auth

To use this API, pass the parameters specified in the 'RequestDropinModuleModel' in a POST request. Those will be: 'module.' 'extUserId' or 'userId,' and 'mode' if the DIM is 'precisionPay' and 'redirectUri' if the DIM is 'connect.'

Body parameter

{
  "module": "loan-servicers-login",
  "redirectUri": "https://127.0.0.1:4200/extract-token",
  "extUserId": "158a3456-5eda-429c-8759-b20f9493d841"
}

Parameters

Name In Type Required Description
body body RequestDropinModuleModel true Post the drop-in module related data along with a registered redirectUri

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "url": "https://dev-dim-api.spinwheel.io/dim?token=-GqXGi2U3W",
    "expiresAt": 1614620029996
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}
{
  "status": {
    "code": 400,
    "desc": "userId is required in body"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful RequestDropinModuleResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Retrieve a DIM Token to Invoke a Drop-In Module by CDN

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/v1/dim/token',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/v1/dim/token', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/v1/dim/token \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "extUserId": "158a3456-5eda-429c-8759-b20f9493d841"
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/v1/dim/token',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /v1/dim/token

To retrieve DIM a token, simply pass either an 'extUserId' or a 'userId' in the body of a POST method to the above endpoint.

Body parameter

{
  "extUserId": "158a3456-5eda-429c-8759-b20f9493d841"
}

Parameters

Name In Type Required Description
body body RequestDropinModuleTokenModel true Post the drop-in module related data

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6ImNvbnN1bWVyX2FwcCIsInJlZGlyZWN0VXJpIjoiaHR0cHM6Ly8xMjcuMC4wLjE6NDIwMC9leHRyYWN0LXRva2VuIiwic2tpcEV4aXN0aW5nVXNlckNoZWNrIjpmYWxzZSwiaWF0IjoxNjE0NjE2MzIwLCJleHAiOjE2MTQ2MTk5MjB9.ORQRGoZ9I1NadcasPmIO6_2YaYr2RNZ__CNZRsVwRPw"
  }
}

Invalid request.

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}
{
  "status": {
    "code": 400,
    "desc": "userId is required in body"
  }
}

Unauthorized

{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful RequestDropinModuleTokenResponseModel
400 Bad Request Invalid request. StatusResponseModel
401 Unauthorized Unauthorized StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Sandbox

Collection of APIs available only on sandbox environment

Trigger student loan payment processing

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {secretKey}'
}

result = RestClient.post 'http://localhost:8080/sandbox/payments/process',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer {secretKey}'
}

r = requests.post('http://localhost:8080/sandbox/payments/process', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080/sandbox/payments/process \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {secretKey}'

const inputBody = '{
  "transactionRequestId": "string"
}';
const headers = {
  'Content-Type':'application/json',
  'Accept':'application/json',
  'Authorization':'Bearer {secretKey}'
};

fetch('http://localhost:8080/sandbox/payments/process',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST /sandbox/payments/process

This API will allow you to trigger the processing of a single student loan transaction.

Body parameter

{
  "transactionRequestId": "string"
}

Parameters

Name In Type Required Description
body body any true Trigger payment processing with requestId or transactionRequestId

Example responses

Successful

{
  "status": {
    "code": 200,
    "desc": "success"
  },
  "data": "Payment processing triggered."
}

Invalid request. (Failed to validate one or more properties of request sent)

{
  "status": {
    "code": 400,
    "desc": "{{errorDescription}}"
  }
}

Unauthorized or Invalid user connection

{
  "status": {
    "code": 401,
    "desc": "Invalid private key"
  }
}

Server side error

{
  "status": {
    "code": 500,
    "desc": "{{errorDescription}}"
  }
}

Responses

Status Meaning Description Schema
200 OK Successful ProcessPaymentResponse
400 Bad Request Invalid request. (Failed to validate one or more properties of request sent) StatusResponseModel
401 Unauthorized Unauthorized or Invalid user connection StatusResponseModel
500 Internal Server Error Server side error StatusResponseModel

Webhooks

Webhooks allows the partner to receive updates for subscribed events.
Setup : The partner needs to configure a URL and the headers partner requires with request.
Note : This section contains the requests partners will receive (with the configured headers) on the provided url.

Following are the sample requests for available webhooks.

Code samples

require 'rest-client'
require 'json'

headers = {
  'Content-Type' => 'application/json'
}

result = RestClient.post 'http://localhost:8080{partnerConfiguredUrl}',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Content-Type': 'application/json'
}

r = requests.post('http://localhost:8080{partnerConfiguredUrl}', headers = headers)

print(r.json())

# You can also use wget
curl -X POST http://localhost:8080{partnerConfiguredUrl} \
  -H 'Content-Type: application/json'

const inputBody = '{
  "eventType": "USER_PAYMENT_STATUS",
  "liabilityType": "STUDENT_LOAN",
  "status": "SCHEDULED",
  "requestId": "{{requestId}}",
  "transactionRequestId": "e8bcb700-7af5-4e79-af2c-4b48152288d8",
  "userId": "c92d173c-1863-4eee-a561-a865d2cfd7a4",
  "statusComment": "Scheduled payment towards loan account"
}';
const headers = {
  'Content-Type':'application/json'
};

fetch('http://localhost:8080{partnerConfiguredUrl}',
{
  method: 'POST',
  body: inputBody,
  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

POST {partnerConfiguredUrl}

Body parameter

{
  "eventType": "USER_PAYMENT_STATUS",
  "liabilityType": "STUDENT_LOAN",
  "status": "SCHEDULED",
  "requestId": "{{requestId}}",
  "transactionRequestId": "e8bcb700-7af5-4e79-af2c-4b48152288d8",
  "userId": "c92d173c-1863-4eee-a561-a865d2cfd7a4",
  "statusComment": "Scheduled payment towards loan account"
}

Parameters

Name In Type Required Description
body body any false Updates for student loan transaction status changes.

Responses

Status Meaning Description Schema

Schemas

StatusModel

{
  "code": 0,
  "desc": "string"
}

Properties

Name Type Required Restrictions Description
code integer(int32) false none Request status code
desc string false none Request status description

StatusResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none

AllocationLoansModel

[
  {
    "loanId": "string",
    "percentage": 0
  }
]

Properties

Name Type Required Restrictions Description
loanId string true none Spinwheel generated loan id of the loan you're posting the payment to.
percentage integer(int32) true none Percentage of the amount you're posting towards the loan.

RequestDropinModuleModel

{
  "module": "loan-servicers-login",
  "mode": "recurring",
  "redirectUri": "string",
  "extUserId": "string",
  "userId": "string"
}

Properties

Name Type Required Restrictions Description
module string true none none
mode string false none Required only when invoking precision-pay drop-in module
redirectUri string true none none
extUserId string true none Required only for connect drop-in. Either extUserId or userId is required
userId string true none Either userId or extUserId is required

Enumerated Values

Property Value
module loan-servicers-login
module transaction-history
module precision-pay
module loan-pal
module loan-list
mode recurring
mode oneTime
mode roundUp

RequestDropinModuleTokenModel

{
  "extUserId": "string",
  "userId": "string"
}

Properties

Name Type Required Restrictions Description
extUserId string true none Required only for connect drop-in. Either extUserId or userId is required
userId string true none Either userId or extUserId is required

RequestDropinModuleResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "url": "string",
    "expiresAt": 0
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data object false none none
» url string false none none
» expiresAt integer(int32) false none none

RequestDropinModuleTokenResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "token": "string"
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data object false none none
» token string false none none

LoanServicerModel

{
  "isIntegrated": true,
  "_id": "string",
  "forgotPasswordUrl": "string",
  "logoUrl": "string",
  "loginUrl": "string",
  "name": "string"
}

Properties

Name Type Required Restrictions Description
isIntegrated boolean false none none
_id string false none none
forgotPasswordUrl string false none none
logoUrl string false none none
loginUrl string false none none
name string false none none

GetLoanServicerByIdResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "isIntegrated": true,
    "_id": "string",
    "forgotPasswordUrl": "string",
    "logoUrl": "string",
    "loginUrl": "string",
    "name": "string"
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data LoanServicerModel false none none

GetLoanServicersResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "isIntegrated": true,
      "_id": "string",
      "forgotPasswordUrl": "string",
      "logoUrl": "string",
      "loginUrl": "string",
      "name": "string"
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [LoanServicerModel] false none none

StudentLoanProfileModel

{
  "name": "string",
  "email": "string",
  "phoneNumber": "string",
  "zipCode": "string"
}

Properties

Name Type Required Restrictions Description
name string false none none
email string false none none
phoneNumber string false none none
zipCode string false none none

LoanModel

{
  "createdOn": 0,
  "loanName": "string",
  "dueDate": "string",
  "loanType": "string",
  "status": "string",
  "loanStatusEndDate": "string",
  "guarantor": "string",
  "origAmount": 0,
  "interestRate": 0,
  "interestRateType": "string",
  "interestRateDiscount": 0,
  "interestRateEffectiveDate": "string",
  "principalBalance": 0,
  "outstandingInterest": 0,
  "regularMonthlyPayment": 0,
  "repaymentPlan": "string",
  "outstandingBalance": 0,
  "loanId": "string",
  "pendingLoanTermInMonths": 0,
  "calculatedPriorityScore": 0,
  "groupName": "string",
  "loanOriginationDate": "string",
  "disbursementDates": [
    "string"
  ],
  "expectedPayoffDate": "string",
  "lastPaymentDate": "string",
  "lastPayment": 0,
  "isOverdue": true,
  "minimumPaymentAmount": 0,
  "ytdInterestPaid": 0,
  "ytdPrincipalPaid": 0,
  "isSubsidizedDerived": true,
  "loanSubsidyScheme": "string"
}

Properties

Name Type Required Restrictions Description
createdOn integer(int64) false none none
loanName string false none none
dueDate string false none none
loanType string false none none
status string false none none
loanStatusEndDate string false none none
guarantor string false none none
origAmount number false none none
interestRate number false none none
interestRateType string false none none
interestRateDiscount number false none none
interestRateEffectiveDate string false none none
principalBalance number false none none
outstandingInterest number false none none
regularMonthlyPayment number false none none
repaymentPlan string false none none
outstandingBalance number false none none
loanId string false none none
pendingLoanTermInMonths integer(int32) false none none
calculatedPriorityScore integer(int32) false none none
groupName string false none none
loanOriginationDate string false none none
disbursementDates [string] false none none
expectedPayoffDate string false none none
lastPaymentDate string false none none
lastPayment number false none none
isOverdue boolean false none none
minimumPaymentAmount number false none none
ytdInterestPaid number false none none
ytdPrincipalPaid number false none none
isSubsidizedDerived boolean false none none
loanSubsidyScheme string false none none

PaymentModel

{
  "paymentDate": "string",
  "paymentAmount": 0,
  "appliedPrincipal": 0,
  "appliedInterest": 0,
  "appliedFees": 0,
  "paymentType": "string"
}

Properties

Name Type Required Restrictions Description
paymentDate string false none none
paymentAmount number false none none
appliedPrincipal number false none none
appliedInterest number false none none
appliedFees number false none none
paymentType string false none none

StudentLoanSummaryModel

{
  "outstandingBalance": 0,
  "dueDate": "string",
  "amountDue": 0,
  "lastPayment": 0,
  "lastPaymentDate": "string",
  "interestRate": 0,
  "regularMonthlyPayment": 0,
  "pendingLoanTermInMonths": 0,
  "originalPrincipalAmount": 0,
  "principalBalance": 0,
  "recommendedLoanId": "string",
  "lastStatementIssueDate": "string",
  "lastStatementBalance": 0,
  "paymentReferenceNumber": 0
}

Properties

Name Type Required Restrictions Description
outstandingBalance number false none none
dueDate string false none none
amountDue number false none none
lastPayment number false none none
lastPaymentDate string false none none
interestRate number false none none
regularMonthlyPayment number false none none
pendingLoanTermInMonths integer(int32) false none none
originalPrincipalAmount number false none none
principalBalance number false none none
recommendedLoanId string false none none
lastStatementIssueDate string false none none
lastStatementBalance number false none none
paymentReferenceNumber number false none none

StudentLoanAccountSummaryModel

{
  "recommendedLoanId": "string",
  "numberOfLoanServicers": 0,
  "numberOfLoanAccounts": 0,
  "outstandingBalance": 0,
  "outstandingInterest": 0,
  "regularMonthlyPayment": 0,
  "pendingLoanTermInMonths": 0,
  "interestRate": 0,
  "originalPrincipalAmount": 0,
  "principalBalance": 0
}

Properties

Name Type Required Restrictions Description
recommendedLoanId string false none none
numberOfLoanServicers integer(int32) false none none
numberOfLoanAccounts integer(int32) false none none
outstandingBalance number false none none
outstandingInterest number false none none
regularMonthlyPayment number false none none
pendingLoanTermInMonths integer(int32) false none none
interestRate number false none none
originalPrincipalAmount number false none none
principalBalance number false none none

ConnectionStatusModel

{
  "isAuthSuccessful": true,
  "lastAuthAttemptOn": 0,
  "lastSuccessfulAuthOn": 0,
  "statusCode": 0,
  "isValid": true,
  "isSecurityStepPending": true,
  "description": "string",
  "userAction": 0
}

Properties

Name Type Required Restrictions Description
isAuthSuccessful boolean false none none
lastAuthAttemptOn integer(int64) false none none
lastSuccessfulAuthOn integer(int64) false none none
statusCode integer(int32) false none none
isValid boolean false none none
isSecurityStepPending boolean false none none
description string false none none
userAction number(nullable) false none none

StudentLoanAccountModel

{
  "connectionStatus": {
    "isAuthSuccessful": true,
    "lastAuthAttemptOn": 0,
    "lastSuccessfulAuthOn": 0,
    "statusCode": 0,
    "isValid": true,
    "isSecurityStepPending": true,
    "description": "string",
    "userAction": 0
  },
  "loanAccounts": [
    {
      "loans": [
        {
          "createdOn": 0,
          "loanName": "string",
          "dueDate": "string",
          "loanType": "string",
          "status": "string",
          "loanStatusEndDate": "string",
          "guarantor": "string",
          "origAmount": 0,
          "interestRate": 0,
          "interestRateType": "string",
          "interestRateDiscount": 0,
          "interestRateEffectiveDate": "string",
          "principalBalance": 0,
          "outstandingInterest": 0,
          "regularMonthlyPayment": 0,
          "repaymentPlan": "string",
          "outstandingBalance": 0,
          "loanId": "string",
          "pendingLoanTermInMonths": 0,
          "calculatedPriorityScore": 0,
          "groupName": "string",
          "loanOriginationDate": "string",
          "disbursementDates": [
            "string"
          ],
          "expectedPayoffDate": "string",
          "lastPaymentDate": "string",
          "lastPayment": 0,
          "isOverdue": true,
          "minimumPaymentAmount": 0,
          "ytdInterestPaid": 0,
          "ytdPrincipalPaid": 0,
          "isSubsidizedDerived": true,
          "loanSubsidyScheme": "string"
        }
      ],
      "payments": [
        {
          "paymentDate": "string",
          "paymentAmount": 0,
          "appliedPrincipal": 0,
          "appliedInterest": 0,
          "appliedFees": 0,
          "paymentType": "string"
        }
      ],
      "summary": {
        "outstandingBalance": 0,
        "dueDate": "string",
        "amountDue": 0,
        "lastPayment": 0,
        "lastPaymentDate": "string",
        "interestRate": 0,
        "regularMonthlyPayment": 0,
        "pendingLoanTermInMonths": 0,
        "originalPrincipalAmount": 0,
        "principalBalance": 0,
        "recommendedLoanId": "string",
        "lastStatementIssueDate": "string",
        "lastStatementBalance": 0,
        "paymentReferenceNumber": 0
      },
      "name": "string",
      "loanAccountId": "string"
    }
  ],
  "updatedOn": 0,
  "createdOn": 0,
  "profile": {
    "name": "string",
    "email": "string",
    "phoneNumber": "string",
    "zipCode": "string"
  },
  "summary": {
    "outstandingBalance": 0,
    "dueDate": "string",
    "amountDue": 0,
    "lastPayment": 0,
    "lastPaymentDate": "string",
    "interestRate": 0,
    "regularMonthlyPayment": 0,
    "pendingLoanTermInMonths": 0,
    "originalPrincipalAmount": 0,
    "principalBalance": 0,
    "recommendedLoanId": "string",
    "lastStatementIssueDate": "string",
    "lastStatementBalance": 0,
    "paymentReferenceNumber": 0
  },
  "loanServicerId": "string",
  "accountId": "string"
}

Properties

Name Type Required Restrictions Description
connectionStatus ConnectionStatusModel false none none
loanAccounts [object] false none none
» loans [LoanModel] false none none
» payments [PaymentModel] false none none
» summary StudentLoanSummaryModel false none none
» name string false none none
» loanAccountId string false none none
updatedOn integer(int64) false none none
createdOn integer(int64) false none none
profile StudentLoanProfileModel false none none
summary StudentLoanSummaryModel false none none
loanServicerId string false none none
accountId string false none none

UserInfoSuccessResponse

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "_id": "string",
    "extUserId": "string",
    "partnerId": "string",
    "studentLoanAccountSummary": {
      "recommendedLoanId": "string",
      "numberOfLoanServicers": 0,
      "numberOfLoanAccounts": 0,
      "outstandingBalance": 0,
      "outstandingInterest": 0,
      "regularMonthlyPayment": 0,
      "pendingLoanTermInMonths": 0,
      "interestRate": 0,
      "originalPrincipalAmount": 0,
      "principalBalance": 0
    },
    "studentLoanAccounts": [
      {
        "connectionStatus": {
          "isAuthSuccessful": true,
          "lastAuthAttemptOn": 0,
          "lastSuccessfulAuthOn": 0,
          "statusCode": 0,
          "isValid": true,
          "isSecurityStepPending": true,
          "description": "string",
          "userAction": 0
        },
        "loanAccounts": [
          {
            "loans": [
              {
                "createdOn": 0,
                "loanName": "string",
                "dueDate": "string",
                "loanType": "string",
                "status": "string",
                "loanStatusEndDate": "string",
                "guarantor": "string",
                "origAmount": 0,
                "interestRate": 0,
                "interestRateType": "string",
                "interestRateDiscount": 0,
                "interestRateEffectiveDate": "string",
                "principalBalance": 0,
                "outstandingInterest": 0,
                "regularMonthlyPayment": 0,
                "repaymentPlan": "string",
                "outstandingBalance": 0,
                "loanId": "string",
                "pendingLoanTermInMonths": 0,
                "calculatedPriorityScore": 0,
                "groupName": "string",
                "loanOriginationDate": "string",
                "disbursementDates": [
                  "string"
                ],
                "expectedPayoffDate": "string",
                "lastPaymentDate": "string",
                "lastPayment": 0,
                "isOverdue": true,
                "minimumPaymentAmount": 0,
                "ytdInterestPaid": 0,
                "ytdPrincipalPaid": 0,
                "isSubsidizedDerived": true,
                "loanSubsidyScheme": "string"
              }
            ],
            "payments": [
              {
                "paymentDate": "string",
                "paymentAmount": 0,
                "appliedPrincipal": 0,
                "appliedInterest": 0,
                "appliedFees": 0,
                "paymentType": "string"
              }
            ],
            "summary": {
              "outstandingBalance": 0,
              "dueDate": "string",
              "amountDue": 0,
              "lastPayment": 0,
              "lastPaymentDate": "string",
              "interestRate": 0,
              "regularMonthlyPayment": 0,
              "pendingLoanTermInMonths": 0,
              "originalPrincipalAmount": 0,
              "principalBalance": 0,
              "recommendedLoanId": "string",
              "lastStatementIssueDate": "string",
              "lastStatementBalance": 0,
              "paymentReferenceNumber": 0
            },
            "name": "string",
            "loanAccountId": "string"
          }
        ],
        "updatedOn": 0,
        "createdOn": 0,
        "profile": {
          "name": "string",
          "email": "string",
          "phoneNumber": "string",
          "zipCode": "string"
        },
        "summary": {
          "outstandingBalance": 0,
          "dueDate": "string",
          "amountDue": 0,
          "lastPayment": 0,
          "lastPaymentDate": "string",
          "interestRate": 0,
          "regularMonthlyPayment": 0,
          "pendingLoanTermInMonths": 0,
          "originalPrincipalAmount": 0,
          "principalBalance": 0,
          "recommendedLoanId": "string",
          "lastStatementIssueDate": "string",
          "lastStatementBalance": 0,
          "paymentReferenceNumber": 0
        },
        "loanServicerId": "string",
        "accountId": "string"
      }
    ],
    "createdOn": 0,
    "updatedOn": 0,
    "isBorrower": true
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data object false none none
» _id string false none none
» extUserId string false none none
» partnerId string false none none
» studentLoanAccountSummary StudentLoanAccountSummaryModel false none none
» studentLoanAccounts [StudentLoanAccountModel] false none none
» createdOn integer(int64) false none none
» updatedOn integer(int64) false none none
» isBorrower boolean false none none

BankAccountDetailsModel

{
  "routingNumber": "string",
  "accountNumber": "string",
  "nameOnAccount": "string",
  "institutionName": "string",
  "accountType": "string",
  "accountHolderProfile": {
    "country": "string",
    "city": "string",
    "zipCode": "string",
    "email": "string",
    "phone": "string",
    "addressLine1": "string"
  }
}

Properties

Name Type Required Restrictions Description
routingNumber string false none none
accountNumber string false none none
nameOnAccount string false none none
institutionName string false none none
accountType string false none none
accountHolderProfile object false none none
» country string false none none
» city string false none none
» zipCode string false none none
» email string false none none
» phone string false none none
» addressLine1 string false none none

PayerModel

{
  "payerId": "string",
  "userId": "string",
  "payerType": "USER",
  "accountLast4Digits": "string",
  "institutionName": "string",
  "createdOn": 0,
  "updatedOn": 0
}

Properties

Name Type Required Restrictions Description
payerId string false none none
userId string false none Will be available only for payerType 'USER'.
payerType string false none none
accountLast4Digits string false none none
institutionName string false none none
createdOn integer(int64) false none none
updatedOn integer(int64) false none none

Enumerated Values

Property Value
payerType USER
payerType PARTNER
payerType PLATFORM

CreatePayerUserModel

{
  "userId": "string",
  "bankAccountDetails": {
    "routingNumber": "string",
    "accountNumber": "string",
    "nameOnAccount": "string",
    "institutionName": "string",
    "accountType": "string",
    "accountHolderProfile": {
      "country": "string",
      "city": "string",
      "zipCode": "string",
      "email": "string",
      "phone": "string",
      "addressLine1": "string"
    }
  }
}

Properties

Name Type Required Restrictions Description
userId string true none none
bankAccountDetails BankAccountDetailsModel true none none

CreatePayerPartnerModel

{
  "bankAccountDetails": {
    "routingNumber": "string",
    "accountNumber": "string",
    "nameOnAccount": "string",
    "institutionName": "string",
    "accountType": "string",
    "accountHolderProfile": {
      "country": "string",
      "city": "string",
      "zipCode": "string",
      "email": "string",
      "phone": "string",
      "addressLine1": "string"
    }
  }
}

Properties

Name Type Required Restrictions Description
bankAccountDetails BankAccountDetailsModel true none none

GetPayersResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "payerId": "string",
      "userId": "string",
      "payerType": "USER",
      "accountLast4Digits": "string",
      "institutionName": "string",
      "createdOn": 0,
      "updatedOn": 0
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [PayerModel] false none none

PayerResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "payerId": "string",
    "userId": "string",
    "payerType": "USER",
    "accountLast4Digits": "string",
    "institutionName": "string",
    "createdOn": 0,
    "updatedOn": 0
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data PayerModel false none none

CreateUseOfFundsModel

{
  "userId": "string",
  "highImpact": false,
  "applyTowardsStatement": false,
  "allocation": [
    {
      "liabilityType": "STUDENT_LOAN",
      "loanServicerId": "string",
      "loanAccountId": "string",
      "percentage": 0,
      "loans": [
        {
          "loanId": "string",
          "percentage": 0
        }
      ]
    }
  ]
}

Properties

Name Type Required Restrictions Description
userId string true none Spinwheel generated User Id of the user you're posting the payment to.
highImpact boolean false none Whether or not the funds to be applied towards liability with highest impact.
applyTowardsStatement boolean false none Whether or not the additional payment made is applied to the statement
allocation AllocationModel false none none

UseOfFundsRequestModel

{
  "highImpact": false,
  "applyTowardsStatement": false,
  "allocation": [
    {
      "liabilityType": "STUDENT_LOAN",
      "loanServicerId": "string",
      "loanAccountId": "string",
      "percentage": 0,
      "loans": [
        {
          "loanId": "string",
          "percentage": 0
        }
      ]
    }
  ]
}

Properties

Name Type Required Restrictions Description
highImpact boolean false none Whether or not the funds to be applied towards liability with highest impact.
applyTowardsStatement boolean false none Whether or not the additional payment made is applied to the statement
allocation AllocationModel false none none

AllocationModel

[
  {
    "liabilityType": "STUDENT_LOAN",
    "loanServicerId": "string",
    "loanAccountId": "string",
    "percentage": 0,
    "loans": [
      {
        "loanId": "string",
        "percentage": 0
      }
    ]
  }
]

Properties

Name Type Required Restrictions Description
liabilityType string true none Type of liability towards which the useOfFunds is allocated.
loanServicerId string true none Spinwheel generated loan servicer id of the loan you're posting the payment to.
loanAccountId string true none Spinwheel generated loan account id of the loan you're posting the payment to.
percentage integer(int32) true none Percentage of the amount you're posting towards the loan account.
loans AllocationLoansModel false none none

Enumerated Values

Property Value
liabilityType STUDENT_LOAN

UseOfFundsModel

{
  "useOfFundsId": "string",
  "userId": "string",
  "partnerId": "string",
  "highImpact": true,
  "applyTowardsStatement": true,
  "allocation": [
    {
      "liabilityType": "STUDENT_LOAN",
      "loanServicerId": "string",
      "loanAccountId": "string",
      "percentage": 0,
      "loans": [
        {
          "loanId": "string",
          "percentage": 0
        }
      ]
    }
  ],
  "createdOn": 0,
  "updatedOn": 0
}

Properties

Name Type Required Restrictions Description
useOfFundsId string false none none
userId string false none none
partnerId string false none none
highImpact boolean false none none
applyTowardsStatement boolean false none none
allocation AllocationModel false none none
createdOn integer(int64) false none none
updatedOn integer(int64) false none none

UseOfFundsResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "useOfFundsId": "string",
    "userId": "string",
    "partnerId": "string",
    "highImpact": true,
    "applyTowardsStatement": true,
    "allocation": [
      {
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "string",
        "loanAccountId": "string",
        "percentage": 0,
        "loans": [
          {
            "loanId": "string",
            "percentage": 0
          }
        ]
      }
    ],
    "createdOn": 0,
    "updatedOn": 0
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data UseOfFundsModel false none none

GetUseOfFundsResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "useOfFundsId": "string",
      "userId": "string",
      "partnerId": "string",
      "highImpact": true,
      "applyTowardsStatement": true,
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "string",
          "loanAccountId": "string",
          "percentage": 0,
          "loans": [
            {
              "loanId": "string",
              "percentage": 0
            }
          ]
        }
      ],
      "createdOn": 0,
      "updatedOn": 0
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [UseOfFundsModel] false none none

TransactionResponseModel

{
  "transactionId": "string",
  "amount": 0,
  "status": "string",
  "requestId": "string",
  "extRequestId": "string",
  "payerId": "string",
  "userId": "string",
  "useOfFunds": {
    "liabilityType": "string",
    "percentage": 0,
    "loanServicerId": "string",
    "loanAccountId": "string",
    "loans": [
      {
        "loanId": "string",
        "percentage": 0
      }
    ],
    "applyTowardsStatement": true
  },
  "scheduleTs": 0,
  "createdOn": 0,
  "updatedOn": 0
}

Properties

Name Type Required Restrictions Description
transactionId string false none none
amount number false none none
status string false none none
requestId string false none none
extRequestId string false none none
payerId string false none none
userId string false none none
useOfFunds object false none none
» liabilityType string false none none
» percentage number false none none
» loanServicerId string false none none
» loanAccountId string false none none
» loans AllocationLoansModel false none none
» applyTowardsStatement boolean false none none
scheduleTs integer(int64) false none none
createdOn integer(int64) false none none
updatedOn integer(int64) false none none

GetTransactionByIdResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "transactionId": "string",
    "amount": 0,
    "status": "string",
    "requestId": "string",
    "extRequestId": "string",
    "payerId": "string",
    "userId": "string",
    "useOfFunds": {
      "liabilityType": "string",
      "percentage": 0,
      "loanServicerId": "string",
      "loanAccountId": "string",
      "loans": [
        {
          "loanId": "string",
          "percentage": 0
        }
      ],
      "applyTowardsStatement": true
    },
    "scheduleTs": 0,
    "createdOn": 0,
    "updatedOn": 0
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data TransactionResponseModel false none none

GetTransactionsResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "transactionId": "string",
      "amount": 0,
      "status": "string",
      "requestId": "string",
      "extRequestId": "string",
      "payerId": "string",
      "userId": "string",
      "useOfFunds": {
        "liabilityType": "string",
        "percentage": 0,
        "loanServicerId": "string",
        "loanAccountId": "string",
        "loans": [
          {
            "loanId": "string",
            "percentage": 0
          }
        ],
        "applyTowardsStatement": true
      },
      "scheduleTs": 0,
      "createdOn": 0,
      "updatedOn": 0
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [TransactionResponseModel] false none none

ScheduleModel

{
  "startDate": "2019-08-24",
  "endDate": "2019-08-24",
  "dayOfTheMonth": 1,
  "isLastDayOfMonth": false
}

If not provided, Current date will be taken as the date for posting recurring transactions.

Properties

Name Type Required Restrictions Description
startDate string(date) false none Date (In YYYY-MM-DD format) to determine when the recurring payments to be started. Defaults to current date.
endDate string(date) false none Date (In YYYY-MM-DD format) to determine when the recurring payments to be ended.
dayOfTheMonth integer(int32) false none An integer value between 1-31
isLastDayOfMonth boolean false none Whether or not the transaction is to be on scheduled last day of every month.

PaymentRequestModel

{
  "requestId": "string",
  "extRequestId": "string",
  "amount": 0,
  "payerId": "string",
  "userId": "string",
  "useOfFundsId": "string",
  "useOfFunds": {
    "highImpact": false,
    "applyTowardsStatement": false,
    "allocation": [
      {
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "string",
        "loanAccountId": "string",
        "percentage": 0,
        "loans": [
          {
            "loanId": "string",
            "percentage": 0
          }
        ]
      }
    ]
  },
  "scheduleTs": 0,
  "requestType": "ONE_TIME",
  "schedule": {
    "startDate": "2019-08-24",
    "endDate": "2019-08-24",
    "dayOfTheMonth": 1,
    "isLastDayOfMonth": false
  },
  "createdOn": 0,
  "updatedOn": 0
}

Properties

Name Type Required Restrictions Description
requestId string false none none
extRequestId string false none none
amount integer(int32) false none recurringTransaction amount.
payerId string false none payerId of payer from whom the payment would go through.
userId string false none none
useOfFundsId string false none useOfFundsId of funds allocation you've configured.
useOfFunds UseOfFundsRequestModel false none none
scheduleTs integer(int64) false none Epoch timestamp value of schedule date
requestType string false none Whether the payment request is one-time or recurring.
schedule ScheduleModel false none If not provided, Current date will be taken as the date for posting recurring transactions.
createdOn integer(int64) false none none
updatedOn integer(int64) false none none

Enumerated Values

Property Value
requestType ONE_TIME
requestType RECURRING

CreateRequestModel

{
  "extRequestId": "string",
  "amount": 0,
  "payerId": "string",
  "userId": "string",
  "useOfFundsId": "string",
  "useOfFunds": {
    "highImpact": false,
    "applyTowardsStatement": false,
    "allocation": [
      {
        "liabilityType": "STUDENT_LOAN",
        "loanServicerId": "string",
        "loanAccountId": "string",
        "percentage": 0,
        "loans": [
          {
            "loanId": "string",
            "percentage": 0
          }
        ]
      }
    ]
  },
  "scheduleTs": 0,
  "schedule": {
    "startDate": "2019-08-24",
    "endDate": "2019-08-24",
    "dayOfTheMonth": 1,
    "isLastDayOfMonth": false
  },
  "requestType": "ONE_TIME"
}

Properties

Name Type Required Restrictions Description
extRequestId string true none An id generated by you, a partner, to refer back the payment request. (Needs to be unique for a given request)
amount integer(int32) true none recurringTransaction amount.
payerId string true none payerId of payer from whom the payment would go through.
userId string true none Spinwheel userId of the user you're posting the recurringTransaction towards.
useOfFundsId string false none useOfFundsId of funds allocation you've configured. Either of useOfFundsId or useOfFunds object is required.
useOfFunds UseOfFundsRequestModel false none none
scheduleTs integer(int64) false none Epoch timestamp value of schedule date
schedule ScheduleModel false none If not provided, Current date will be taken as the date for posting recurring transactions.
requestType string true none Whether the payment request is one-time or recurring.

Enumerated Values

Property Value
requestType ONE_TIME
requestType RECURRING

PaymentRequestResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "requestId": "string",
    "extRequestId": "string",
    "amount": 0,
    "payerId": "string",
    "userId": "string",
    "useOfFundsId": "string",
    "useOfFunds": {
      "highImpact": false,
      "applyTowardsStatement": false,
      "allocation": [
        {
          "liabilityType": "STUDENT_LOAN",
          "loanServicerId": "string",
          "loanAccountId": "string",
          "percentage": 0,
          "loans": [
            {
              "loanId": "string",
              "percentage": 0
            }
          ]
        }
      ]
    },
    "scheduleTs": 0,
    "requestType": "ONE_TIME",
    "schedule": {
      "startDate": "2019-08-24",
      "endDate": "2019-08-24",
      "dayOfTheMonth": 1,
      "isLastDayOfMonth": false
    },
    "createdOn": 0,
    "updatedOn": 0
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data PaymentRequestModel false none none

GetPaymentRequestsResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "requestId": "string",
      "extRequestId": "string",
      "amount": 0,
      "payerId": "string",
      "userId": "string",
      "useOfFundsId": "string",
      "useOfFunds": {
        "highImpact": false,
        "applyTowardsStatement": false,
        "allocation": [
          {
            "liabilityType": "STUDENT_LOAN",
            "loanServicerId": "string",
            "loanAccountId": "string",
            "percentage": 0,
            "loans": [
              {
                "loanId": "string",
                "percentage": 0
              }
            ]
          }
        ]
      },
      "scheduleTs": 0,
      "requestType": "ONE_TIME",
      "schedule": {
        "startDate": "2019-08-24",
        "endDate": "2019-08-24",
        "dayOfTheMonth": 1,
        "isLastDayOfMonth": false
      },
      "createdOn": 0,
      "updatedOn": 0
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [PaymentRequestModel] false none none

ProcessPaymentWithTransactionRequestIdRequest

{
  "transactionRequestId": "string"
}

Properties

Name Type Required Restrictions Description
transactionRequestId string true none transactionRequestId received by you after posting the payment.

ProcessPaymentWithRequestIdRequest

{
  "requestId": "string"
}

Properties

Name Type Required Restrictions Description
requestId string true none requestId sent by you while posting the payment

ProcessPaymentResponse

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": "string"
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data string false none none

RequestExtraPaymentImpactModel

{
  "loanBalance": 0,
  "interestRate": 0,
  "monthlyPayment": 0,
  "remainingLoanTermInMonths": 0,
  "extraMonthlyPayment": 0,
  "isRecurring": true
}

Properties

Name Type Required Restrictions Description
loanBalance integer(int64) true none Total outstanding loan balance
interestRate integer(int64) false none none
monthlyPayment integer(int32) true none none
remainingLoanTermInMonths integer(int32) false none none
extraMonthlyPayment integer(int32) true none none
isRecurring boolean false none none

RequestExtraPaymentImpactResponseModel

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": {
    "monthlyPayment": 0,
    "monthlyPaymentRound": 0,
    "principalSavings": 0,
    "principalSavingsRound": 0,
    "interestSavings": 0,
    "interestSavingsRound": 0,
    "totalSavings": 0,
    "totalSavingsRound": 0,
    "totalSavingsPercentage": 0,
    "durationDebtRelief": {
      "years": 0,
      "months": 0,
      "days": 0,
      "hours": 0,
      "print": "string"
    },
    "daysDebtFree": 0,
    "originalMonthlyPayment": 0,
    "originalMonthlyPaymentRound": 0,
    "originalTotalInterestAmount": 0,
    "originalTotalInterestAmountRound": 0,
    "originalTotalPaymentAmount": 0,
    "originalTotalPaymentAmountRound": 0,
    "originalPaymentDays": 0,
    "monthlyBurndown": [
      {
        "date": "string",
        "interestRate": 0,
        "monthlyInstallment": 0,
        "principalReduction": 0,
        "interest": 0,
        "remainingPrincipal": 0
      }
    ]
  }
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data object false none none
» monthlyPayment number false none none
» monthlyPaymentRound integer(int32) false none none
» principalSavings number false none none
» principalSavingsRound integer(int32) false none none
» interestSavings number false none none
» interestSavingsRound integer(int32) false none none
» totalSavings number false none none
» totalSavingsRound integer(int32) false none none
» totalSavingsPercentage number false none none
» durationDebtRelief object false none none
»» years integer(int32) false none none
»» months integer(int32) false none none
»» days integer(int32) false none none
»» hours integer(int32) false none none
»» print string false none none
» daysDebtFree integer(int32) false none none
» originalMonthlyPayment number false none none
» originalMonthlyPaymentRound integer(int32) false none none
» originalTotalInterestAmount number false none none
» originalTotalInterestAmountRound integer(int32) false none none
» originalTotalPaymentAmount number false none none
» originalTotalPaymentAmountRound integer(int32) false none none
» originalPaymentDays integer(int32) false none none
» monthlyBurndown [object] false none none
»» date string false none none
»» interestRate number false none none
»» monthlyInstallment number false none none
»» principalReduction number false none none
»» interest number false none none
»» remainingPrincipal number false none none

TxnHistoryDataModel

{
  "loanAccountId": "b25f37a3-acaa-4ed3-aa2c-3211bca28f92",
  "loanServicerId": "6daee756-fc64-4494-890f-d427fd648416",
  "paymentAppliedDate": "2021-02-15T18:30:00.000Z",
  "paymentType": "Payment",
  "paymentAmount": 325,
  "appliedPrincipal": 253.57,
  "appliedInterest": 71.43,
  "appliedFees": 0,
  "paymentDetails": [
    {
      "appliedTxnId": "string",
      "appliedToLoans": [
        {
          "loanId": "string",
          "percentage": 0
        }
      ],
      "championUserId": "string",
      "internalDetails": {},
      "externalDetails": {}
    }
  ],
  "isInternalTxn": true,
  "status": "APPLIED",
  "loanServicerName": "Granite State Management & Resources"
}

Properties

Name Type Required Restrictions Description
loanAccountId string false none The id of the loan account.
loanServicerId string false none The id of the loan servicer.
paymentAppliedDate string false none The date of payment.
paymentType string false none The type of payment.
paymentAmount integer false none The amount of payment.
appliedPrincipal integer false none The applied principal of payment.
appliedInterest integer false none The applied interest of payment.
appliedFees integer false none The applied fees of payment.
paymentDetails [object] false none The details of payment indicating the payment is done by platform.
» appliedTxnId string false none none
» appliedToLoans [object] false none none
»» loanId string false none none
»» percentage number false none none
» championUserId string false none none
» internalDetails object false none none
» externalDetails object false none none
isInternalTxn boolean false none none
status string false none none
loanServicerName string false none The name of the loan servicer.

TxnHistorySuccessResponse

{
  "status": {
    "code": 0,
    "desc": "string"
  },
  "data": [
    {
      "loanAccountId": "b25f37a3-acaa-4ed3-aa2c-3211bca28f92",
      "loanServicerId": "6daee756-fc64-4494-890f-d427fd648416",
      "paymentAppliedDate": "2021-02-15T18:30:00.000Z",
      "paymentType": "Payment",
      "paymentAmount": 325,
      "appliedPrincipal": 253.57,
      "appliedInterest": 71.43,
      "appliedFees": 0,
      "paymentDetails": [
        {
          "appliedTxnId": "string",
          "appliedToLoans": [
            {
              "loanId": "string",
              "percentage": 0
            }
          ],
          "championUserId": "string",
          "internalDetails": {},
          "externalDetails": {}
        }
      ],
      "isInternalTxn": true,
      "status": "APPLIED",
      "loanServicerName": "Granite State Management & Resources"
    }
  ]
}

Properties

Name Type Required Restrictions Description
status StatusModel false none none
data [TxnHistoryDataModel] false none none

UserPaymentStatusWebhookModel

{
  "userId": "e1b0d89d-6d86-4ff6-a2ab-46b99e2c1b1e",
  "status": "SCHEDULED",
  "statusComment": "Scheduled payment towards loan account",
  "requestId": "string",
  "transactionRequestId": "c8301530-b77e-46c5-9c03-8d3233ff6b8d",
  "eventType": "USER_PAYMENT_STATUS",
  "liabilityType": "STUDENT_LOAN"
}

Properties

Name Type Required Restrictions Description
userId string false none none
status string false none This field is set to the updated payment status
statusComment string false none Comment regarding the status
requestId string false none Request ID provided by the client when scheduling a payment.
transactionRequestId string false none Transaction ID provided to the client when scheduling a payment.
eventType string false none Name of the webhook
liabilityType string false none Liability type

Enumerated Values

Property Value
status SCHEDULED
status FAILED
status APPLIED
liabilityType STUDENT_LOAN

UserConnectionStatusWebhookModel

{
  "userId": "e1b0d89d-6d86-4ff6-a2ab-46b99e2c1b1e",
  "isValid": true,
  "statusCode": 2000,
  "description": "Auth Successful!",
  "eventType": "USER_CONNECTION_STATUS",
  "liability": "STUDENT_LOAN"
}

Properties

Name Type Required Restrictions Description
userId string false none none
isValid boolean false none This field is set to true if current login credentials are valid else false.
statusCode number false none The status code for current connection status.
description string false none Description of the current connection status.
eventType string false none Name of the webhook
liability string false none Liability type

Enumerated Values

Property Value
liability STUDENT_LOAN