Api documentation

Please note: this documentation is generic, does not take into account the specifics of any particular originator and is provided for illustrative purposes only. When beginning actual integration with Blackmoon, please refer to extended documentation in your own account, which will be provided to you by email.

The API allows you to integrate with the Blackmoon platform. To fully integrate with it you will need to implement three major parts - online loan sending, bulk loan data export and reconciliations.

Online loan sending

This part of the API allows you to send new loans to us as soon as they appear, so we can participate in them. We evaluate the loan, calculate the fee we are willing to pay for it and reply with our decision and calculated fee.

This part is implemented using offers.create and offers.accept/offers.decline/offers.cancel methods.

Bulk loan data export

This part of the API allows you to export loan and repayments data. To do that you will need to implement data.sendApplications and data.sendRepayments methods.

data.sendApplications call schedule:

- every day, as the new day begins, except 1st day of month: all currently open loans + loans closed in last 7 days + all loans ever purchased by Blackmoon investors (even those which were sold back, i.e. loans for which one or more cession transaction exist) regardless of status
- every 1st day of month, as the new day begins: all currently open loans + loans closed in last 32 days + all loans ever purchased by Blackmoon investors (even those which were sold back, i.e. loans for which one or more cession transaction exist) regardless of status

data.sendRepayments call schedule:

- every day, as the new day begins, except 1st day of month: all repayments received during last 7 days + all loans ever purchased by Blackmoon investors (even those which were sold back, i.e. loans for which one or more cession transaction exist) regardless of status
- every 1st day of month, as the new day begins: all repayments received during last 32 days + all loans ever purchased by Blackmoon investors (even those which were sold back, i.e. loans for which one or more cession transaction exist) regardless of status

Notes:
- repayments batch should use not "repayment date", but "date you have found out about this payment" - so if yesterday you find out about some repayment made 3 months ago (and as such having repayment date = -3 months), this repayment should appear in yesterday's repayments batch
- you will need to send data for all your loans, including those you have already sent to offers.create - these two methods work independently of each other
- data for all loans should be sent, including those that were not bought by Blackmoon

Important Notice

No changes​ must be made to the internal automated procedures of servicing and collection of transferred loans.
According to the cooperation agreement with Blackmoon the originator is engaged as the servicer of the transferred loans and the originator undertakes to treat such loans in all ways similar to the loans held on its balance sheet or sold to other investors.

Business Process Overview

Interaction diagram

Working with API

Sending requests

All requests are sent via HTTPS using GET or POST by API url (found in your account integration parameters). All parameters are passed as GET or POST parameters. Default method is GET.

Authentication and tokens

To authenticate with API, you need to pass access token with each query (found in integration parameters).

Access token will expire from time to time, so you will need to receive a new one when it does. To automate this, you can use auth.refreshTokens method to receive new access/refresh token pair.

Common parameters
Some of the parameters are common for every query
organizationId - your organization id
accessToken - access token to authenticate yourself
Response

All API responses are json encoded. A response is an object with one two fields, one of each is always 'success' or 'error', and the seconds one, if present, is 'warning'.

Success fields are described in method specification. Warning field contains two fields, 'warningMessage' and 'warningCode'.
Please note that, although warning is not an error, it is a sign that something could be wrong with the query, so you should always check for warningMessage and warningCode in reply, log it and take appropriate actions.

Example of a successful reply with warning
{
    "success":  {
        "offerId":  "8"
    },
    "warning":  {
        "warningMessage":  "This offer has already been accepted",
        "warningCode":  "100"
    }
}
Error response contains error message, error code and all passed request parameters
{
    "error":  {
        "errorMessage":  "Bad access token",
        "errorCode":  2,
        "requestParameters":  {
            "organizationId":  "14",
            "accessToken":  "bad access token"
        }
    }
}
Example
Request
curl 'http://your api url/ping?accessToken=accessToken&organizationId=organizationId'
Response
{
    "success":  {
        "message":  "pong",
        "date":  "2015-01-30T14:47:39+0000",
        "requestParameters":  {
            "organizationId":  "organizationId",
            "accessToken":  "accessToken"
        }
    }
}

Methods

Offers

offers.create

Create an offer for a new loan. Please note that organizationall offers you have received as a result of invoking this method should be either accepted or declined in 1 hour or less, or they will be declined automatically by system.
Offers.create returns updatedDatetime which you should always record as it was returned, since you will use it later during reconciliation.

Sandbox version of this method does not use production calculations and instead performs some random (but repeatable) calculation on your applicationFields. It means that decision and fee are meaningless and are always the same for the same loan. If you want to test both willParticipate values, just change any input value within allowed ranges (i.e. income) in applicationFields until result changes as you like.
The nature of this calculation also results in wildly differing results even when introducing most subtle changes. This is to be expected.

Parameters
applicationId - unique loan id
applicationFields - string containing json encoded array of all loan fields
    fieldName: fieldValidationRule // All loan fields needed for us to assess your loan will be added after examining your data in documentation in your personal API account
Request example
curl 'http://your api url/offers.create?applicationId=22&applicationFields={"income":3000}&organizationId=organizationId&accessToken=accessToken'
Response
{
    "success":  {
        "updatedDatetime":  ISO-formatted date and time when this offer was last updated,
        "willParticipate":  boolean value states whether or not we are interested in participating in this loan,
        "offerId":  unique offer id for loan, zero if willParticipate is false,
        "originationFee":  origination fee in loan currency, zero if willParticipate is false. Origination fee is absolute, so originationFee = 100 for loan amount = €1000 means that origination fee is €100,
        "servicingFee":  servicing fee in percentage, zero if willParticipate is false. Servicing fee is a percentage of cashflow receivable, which would be paid to originator upon payment date. If payment from customer = €100 and servicingFee = 0.1525, then originator receives €15.25 as a servicing fee upon that payment,
        "bidAmount":  bid amount that Blackmoon is ready to make, zero if willParticipate is false (used only for originators which can sell partial loans, in other cases bidAmount will be always equal to loanAmount),
        "loanAmount":  loan amount which was read from applicationFields and for which fee was calculated (in loan currency), so you can double-check it was interpreted correctly by us,
        "investorLegalEntityId":  ID of an investor that bought the loan (integer). You will use this ID to differentiate between different legal entities buying loans from you.
    }
}
offers.accept

Accept previously received offer.
Offers.accept returns updatedDatetime which you should always record as it was returned, since you will use it later during reconciliation.

Parameters
offerId - offer id received from offers.create
comment - description of the cause of action, optional
Request example
curl 'http://your api url/offers.accept?offerId=8&organizationId=organizationId&comment=comment-string&accessToken=accessToken
Response
{
    "success":  {
        "updatedDatetime":  ISO-formatted date and time when this offer was last updated,
        "offerId":  offer id that was passed
    }
}
offers.decline

Decline previously received offer.
Offers.decline returns updatedDatetime which you should always record as it was returned, since you will use it later during reconciliation.

Parameters
offerId - offer id received from offers.create
comment - description of the cause of action, optional
Request example
curl 'http://your api url/offers.decline?offerId=9&organizationId=organizationId&comment=comment-string&accessToken=accessToken'
Response
{
    "success":  {
        "updatedDatetime":  ISO-formatted date and time when this offer was last updated,
        "offerId":  offer id that was passed
    }
}
offers.cancel

Cancel previously accepted offer. Please note that offer cancellation should only be used in special cases, e.g. when user cancels an approved loan, so underlying accepted offer should be cancelled.
Offers.cancel returns updatedDatetime which you should always record as it was returned, since you will use it later during reconciliation.

Parameters
offerId - offer id received from offers.create
comment - description of the cause of action
Request example
curl 'http://your api url/offers.cancel?offerId=9&organizationId=organizationId&comment=comment-string&accessToken=accessToken'
Response
{
    "updatedDatetime":  ISO-formatted date and time when this offer was last updated,
    "offerId":  offer id that was passed
}

Data

data.sendApplications

Send loans data. See schedule in Bulk loan data export section.

Parameters
applications - uploaded file with loans data, in CSV format. First row should contain column headers, all other rows should contain the same number of columns as in header.
Loans file fields:
    applicationId: unique loan id
    fieldName: fieldValidationRule // All loan fields needed for us to assess your loan will be added after examining your data in documentation in your personal API account
Request example
curl -F 'applications=@applications.csv' 'http://your api url/data.sendApplications?organizationId=organizationId&accessToken=accessToken'
Response
{
    "success":  []
}
data.sendRepayments

Send repayments data. See schedule in Bulk loan data export section.

Parameters
repayments - uploaded file with repayments data, in CSV format. First row should contain column headers, all other rows should contain the same number of columns as in header.

Required fields in header are:
    repaymentId - numeric, unique repayment id
    applicationId - numeric, unique loan id
    datetime - repayment date and time in ISO8601 format, example: 2015-05-21T10:20:38+03:00
    amount - numeric, repayment amount
Request example
curl -F 'repayments=@repayments.csv' 'http://your api url/data.sendRepayments?organizationId=organizationId&accessToken=accessToken'
Response
{
    "success":  []
}

Portfolio

portfolio.reconcileDirectCessionTransactions

Reconcile cession transaction on loans bought by Blackmoon investors, for all time. If any discrepancies detected, Blackmoon tech support should be contacted and discrepancies removed.

Please note that we need some time after receiving your data to process it, so the cession transactions might not be ready to reconcile just yet. If that is the case, the API will reply that it is not ready yet and you should retry this request 1 hour later, and again until you receive ready = true in reply.

Parameters
directCessionTransactions - direct cession transactions list (for loans purchased all time)
[
    {
        "investorLegalEntityId":  ID of investor legal entity who owns this loan and with which cession transaction is performed, returned in offers.create,
        "loanId":  ID of the loan participating in this cession transaction,
        "type":  cession transaction type, "direct" or "topup",
        "day":  YYYY-MM-DD-formatted date of cession transaction,
        "price":  cession transaction price,
        "originationFee":  cession transaction origination fee, returned in offers.create,
        "servicingFee":  cession transaction servicing fee, returned in offers.create
    },
    ... // for every cession transaction made for all time
]
Request example
curl 'http://your api url/portfolio.reconcileDirectCessionTransactions?cessionTransactions=[{"investorLegalEntityId":1,"loanId":2,"type":"direct","day":"2016-07-01","price":500.5,"originationFee":20.2,"servicingFee":0.3}]&organizationId=organizationId&accessToken=accessToken'
Response
When Blackmoon calculation is not ready yet:
{
    "success":  {
        "ready":  false
    }
}
When Blackmoon calculation is ready:
{
    "success":  {
        "ready":  true,
        "totalCessionTransactionsCount":  23,
        "correctCessionTransactionsCount":  23,
        "platformOnly":  [],
        "platformMissing":  []
    }
}
Some discrepancies detected:
{
    "success": {
      "ready": true,
      "totalDirectCessionTransactionsCount": 23,
      "correctDirectCessionTransactionsCount": 21,
      "platformOnly": [{ // These cession transactions were not found in request and exist only on Blackmoon platform
        "investorLegalEntityId": 24,
        "loanId": 89,
        "type": "direct"
        "day": "2016-05-30"
        "price": 2450.31
        "originationFee": 99.17,
        "servicingFee": 0.01
      }],
      "platformMissing": [{// These cession transactions were found in request but they do not exist on Blackmoon platform
        "investorLegalEntityId": 24,
        "loanId": 43,
        "type": "direct"
        "day": "2016-06-10"
        "price": 1.99
        "originationFee": 10.00,
        "servicingFee": 1858.39
      },
    }]
}
portfolio.getReverseCessionTransactions

Get full list of loans Blackmoon investor sell back to originator, for all time.

Parameters
none - this method requires no parameters
Request example
curl 'http://your api url/portfolio.getReverseCessionTransactions?organizationId=organizationId&accessToken=accessToken'
Response
When Blackmoon calculation is not ready yet:
{
    "success":  {
        "ready":  false
    }
}
When Blackmoon calculation is ready:
{
    "success": {
        "ready": true,
        "reverseCessionTransactions": [{
            "loanId": 3917867,
            "price": "199.3",
            "type": "UpperThreshold", // Possible values: Overdue, UpperThreshold, LowerThreshold, Cancelled
            "date": "2016-01-01",
        },
        { // An entry for every reverse cession transaction
        ...
        }]
     }
}

Auth

auth.refreshTokens

Refresh both access and refresh tokens.

Parameters
refreshToken - current refresh token
Request example
curl 'http://your api url/auth.refreshTokens?refreshToken=5fad2a5e16113492acdf07b2b12c1dae8da4fc57&organizationId=organizationId&accessToken=accessToken'
Response
{
    "success":  {
        "accessToken":  new access token,
        "refreshToken":  new refresh token,
        "accessTokenExpirationTime":  new access token expiration time
    }
}

Misc

ping

Helper method which replies with a message 'pong', current server date and all parameters that you have sent. Useful to perform connectivity checks and debug tricky parameters.

Request example
curl 'http://your api url/ping?organizationId=organizationId&accessToken=accessToken'
Response
{
    "success":  {
        "message":  pong,
        "date":  current server time,
        "parameters":  [ ...array of all passed parameters ]
    }
}

Error and warning codes

Error codes
1   Validation failed
2   Bad access token
3   Bad refresh token
5   Access token expired
6   This is a sandbox-only method
7   File not valid
8   Error while reading file
9   Offer not found
10   Internal API error
11   Access denied
12   File was not uploaded, possibly too big
100   Unable to decode applicationFields
101   Unable to calculate fee
102   A resolved offer already exists for this loan
103   An offer already exists for this loan
110   Cannot accept, bad offer status
120   Cannot decline, bad offer status
130   No loans file passed
140   No repayments file passed
150   Unable to calculate limits
160   Unable to reconcile offer
170   Unable to cancel offer, bad offer status
Warning codes
100   This offer has already been accepted
101   This offer has already been declined

Compliance documents