Kiipr

Documentation API

This page is a work in progress. It's only available in English for now. We apologize for the inconvenience and are working on making it available in French soon.

Quickstart

Get your first customer summary in a few minutes.

1. Sign up for Kiipr App

If you don't have an account yet, create your business account for Kiipr App first.

2. Create an API key

Log in to Kiipr, open API keys in the menu and create a key. You get a client ID (kpr_ci_…) and a client secret (kpr_cs_…). Copy the secret right away: it's shown only once.

3. Get an access token

Exchange your client ID and secret for an access token, valid for 1 hour.

curl -X POST https://api.kiipr.com/oauth/token \
  -u "kpr_ci_your_client_id:kpr_cs_your_client_secret" \
  -d grant_type=client_credentials
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}

3. Look up a customer

curl -X POST https://api.kiipr.com/api/v1/customer-summary \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-8d4b-4f7a-9c3e-2b5d7e9f1a2c" \
  -d '{
    "firstName": "Jane",
    "lastName": "Doe",
    "dateOfBirth": "1990-01-01",
    "email": "jane@example.com",
    "postalCode": "H2X 1Y4"
  }'

You get the customer's summary back in JSON. See the full response below.


Authentication

The Kiipr API uses OAuth 2.0 with the client credentials grant.

  1. Send your client ID and secret to POST /oauth/token to get an access token.
  2. Send the access token in the Authorization header of each API call:
Authorization: Bearer YOUR_ACCESS_TOKEN

Tokens last 1 hour (expires_in: 3600). Reuse a token for every call until it expires, then request a new one. There's no need to fetch a token per call.

Treat your client secret like a password: keep it on your server, never in a browser or mobile app. You can have up to 5 active API keys and revoke any of them at any time from API keys. Revoking a key, or closing your account, stops its tokens on the very next call.


Pricing & limits

Each endpoint has a flat price in tokens per call, listed with the endpoint below. Tokens are taken from your account balance. You can buy tokens in Kiipr and activate auto-renewal.

Rate limit5 calls per second per account, bursts of 20
Access token lifetime1 hour
Active API keys5

When you're charged

  • A successful call is charged its endpoint's price, even when no matching customer is found.
  • Invalid requests (400) are free. So are rejected calls (401, 402, 429).
  • A call that fails on our side (500) is refunded automatically.
  • Retries are free: send the same Idempotency-Key and the same body to the same endpoint within an hour and you're only charged once.

The rate limit is shared by all the API keys of your account.


Endpoints

Base URL: https://api.kiipr.com

POST /oauth/token

Exchange your API key for an access token.

Request — application/x-www-form-urlencoded

Send your client ID and secret one of two ways, not both:

  • HTTP Basic auth (recommended): Authorization: Basic base64(client_id:client_secret). Most HTTP clients do this for you, e.g. curl -u.
  • Form fields: client_id and client_secret in the body.
FieldDescription
grant_typerequiredAlways client_credentials.
client_idrequired without Basic authYour client ID.
client_secretrequired without Basic authYour client secret.

Response — 200

FieldDescription
access_tokenThe token to send as Authorization: Bearer ….
token_typeAlways Bearer.
expires_inSeconds until the token expires (3600).

POST /api/v1/customer-summary

Price: 10 tokens per call

Returns a summary of a person's history across the Kiipr network. Kiipr matches the person from the identity details you send. The more details you send, the better the match. When no one matches, the call is still charged and returns an empty summary.

Headers

HeaderDescription
AuthorizationrequiredBearer YOUR_ACCESS_TOKEN
Content-Typerequiredapplication/json
Idempotency-KeyoptionalUp to 255 characters, for example a UUID per lookup. Retrying with the same key and body within an hour isn't charged again.

Request body — application/json

FieldDescription
firstNamerequired
lastNamerequired
dateOfBirthrequiredYYYY-MM-DD
socialInsuranceNumberoptional9 digits
emailoptional
primaryPhoneNumberoptional10 digits
postalCodeoptionalCanadian postal code, e.g. H2X 1Y4

Response — 200

{
  "summary": {
    "active_requests": 1,
    "active_loans": 1,
    "total_approved_requests": 4,
    "total_refused_requests": 2,
    "requests_last_7_days": 1,
    "requests_last_30_days": 2,
    "requests_last_90_days": 3,
    "unique_lenders_contacted_last_30_days": 2,
    "total_loans": 4,
    "total_active_loans": 1,
    "total_suspended_loans": 0,
    "total_loans_in_collection": 0,
    "total_loans_in_loss": 0,
    "total_completed_loans": 3,
    "completed_without_incident_count": 2,
    "completed_with_incident_count": 1,
    "loan_average_amount": 850,
    "total_payments": 42,
    "successful_payments": 38,
    "total_payment_errors": 3,
    "total_delayed_payments": 1,
    "payment_error_ratio": 0.071,
    "delayed_payment_ratio": 0.024,
    "has_payment_agreement": false,
    "blacklisted_places_count": 0,
    "has_bankruptcy_filing": false,
    "has_consumer_proposal": false,
    "last_bankruptcy_filing_date": null,
    "last_consumer_proposal_date": null
  },
  "contact": {
    "last_known_address": {
      "line1": "123 Main St",
      "city": "Montréal",
      "province": "QC",
      "country": "Canada",
      "postal_code": "H2X 1Y4"
    },
    "last_phone_number": "5145551234",
    "last_known_employer": {
      "name": "Acme Inc.",
      "address": null
    }
  },
  "requests": {
    "last_3_requests": [
      {
        "status": "evaluation",
        "requested_amount": 1000,
        "requested_at": "2026-10-28",
        "refusal_reason_category": null
      },
      {
        "status": "refused",
        "requested_amount": 1500,
        "requested_at": "2026-10-12",
        "refusal_reason_category": "insufficient-income"
      },
      {
        "status": "completed",
        "requested_amount": 750,
        "requested_at": "2026-06-03",
        "refusal_reason_category": null
      }
    ]
  }
}

When no one matches, you get the same shape with zeros, nulls and an empty last_3_requests.

summary
FieldDescription
active_requestsRequests still in progress (not completed, refused or cancelled).
total_approved_requests / total_refused_requestsRequests approved or refused, all time.
requests_last_7_days / _30_days / _90_daysRequests made in the last 7, 30 and 90 days.
unique_lenders_contacted_last_30_daysNumber of different lenders the person applied to in the last 30 days.
total_loansLoans granted, all time.
total_active_loans (also active_loans) / total_suspended_loans / total_loans_in_collection / total_loans_in_loss / total_completed_loansLoans by current status.
completed_without_incident_count / completed_with_incident_countCompleted loans without, or with, payment errors.
loan_average_amountAverage loan amount, in dollars.
total_paymentsPayments made, failed or late, all loans together.
successful_payments / total_payment_errors / total_delayed_paymentsPayments made, failed and late.
payment_error_ratio / delayed_payment_ratioFailed and late payments as a share of total_payments (0 to 1).
has_payment_agreementWhether the person has a payment agreement on any loan.
blacklisted_places_countNumber of lenders that blacklisted the person.
has_bankruptcy_filing / last_bankruptcy_filing_dateBankruptcy on file, and its most recent date (YYYY-MM-DD or null).
has_consumer_proposal / last_consumer_proposal_dateConsumer proposal on file, and its most recent date (YYYY-MM-DD or null).
contact
FieldDescription
last_known_addressMost recent address on file: line1, city, province, country, postal_code.
last_phone_numberMost recent phone number on file.
last_known_employerMost recent employer: name. address is always null for now.
requests
FieldDescription
last_3_requestsThe 3 most recent requests, newest first: status, requested_amount, requested_at (YYYY-MM-DD) and, for refused requests, refusal_reason_category.

Errors

Errors return JSON with a code for your code and a message for people. Validation errors also list the invalid fields.

{
  "code": "E_VALIDATION_FAILED",
  "message": "E_VALIDATION_FAILED: Validation failed",
  "fields": [
    { "field": "dateOfBirth", "validation": "required", "message": "..." }
  ]
}

Customer summary

StatuscodeMeaningCharged
400E_BAD_REQUESTThe body isn't valid JSON, or Idempotency-Key is over 255 characters.No
400E_VALIDATION_FAILEDA field is missing or invalid. fields says which.No
401—The access token is missing, invalid or expired, its API key was revoked, or the account is closed. Get a new token.No
402E_INSUFFICIENT_TOKENSYour account doesn't have enough tokens. Buy tokens in Kiipr, then retry.No
429—Over 5 calls per second. Wait Retry-After seconds (1), then retry.No
500E_INTERNALSomething failed on our side. Safe to retry with the same Idempotency-Key.Refunded

Access token

The token endpoint follows the OAuth 2.0 standard: errors carry an error field.

StatuserrorMeaning
400invalid_requestgrant_type is missing, or credentials were sent both in Basic auth and in the form.
400unsupported_grant_typegrant_type isn't client_credentials.
401invalid_clientUnknown or revoked client ID, wrong secret, or closed account.
429—Too many token requests. Reuse your token until it expires.