Skip to content
WorkspaceDarkLight
Contents

Get started

Errors

Every error says what went wrong, what kind of thing to do about it, and which request it was.

curl -s -i -X POST https://app.creditcresttechnologies.com.au/v1/capacity \
  -H "authorization: Bearer $CREDITCREST_KEY" \
  -H 'content-type: text/csv' \
  --data-binary @statement.csv
import { readFile } from 'node:fs/promises';

const res = await fetch('https://app.creditcresttechnologies.com.au/v1/capacity', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.CREDITCREST_KEY}`,
    'content-type': 'text/csv',
  },
  body: await readFile('statement.csv'),
});
const answer = await res.json();
if (!res.ok) throw new Error(`${answer.type} ${answer.error}: ${answer.message} Request ${answer.request_id}`);
console.log(answer);
import json, os, urllib.error, urllib.request

req = urllib.request.Request(
    "https://app.creditcresttechnologies.com.au/v1/capacity",
    data=open("statement.csv", "rb").read(),
    headers={
        "Authorization": "Bearer " + os.environ["CREDITCREST_KEY"],
        "Content-Type": "text/csv",
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req) as res:
        print(json.load(res))
except urllib.error.HTTPError as e:
    err = json.load(e)
    print(err["type"], err["error"], err["message"], "Request", err["request_id"])
Response · 400
HTTP/1.1 400 Bad Request
request-id: req_7c1e2a9f04b3d568
creditcrest-version: 2026-10-22
content-type: application/json; charset=utf-8

{
  "error": "bad_request",
  "message": "Capacity needs a loan: send "loan", "rate" and "years" beside "statement".",
  "param": "loan",
  "type": "invalid_request",
  "doc_url": "https://app.creditcresttechnologies.com.au/developers/errors#bad_request",
  "request_id": "req_7c1e2a9f04b3d568"
}

What an error holds

Every /v1 error is JSON. Fields you do not know may be added; pass over them.

Field
errorThe code. Branch on it: a code does not change. Every code is listed below.
typeWhat to do, by status: one of the types below.
messageFor a person: what went wrong and what to send instead. Its words may change, so do not match on them.
paramThe field at fault, where the product knows which: loan, or start.amount inside an object. Not every error has one.
doc_urlThe code’s entry on this page.
request_idThe same as the Request-Id header. Quote it to support.
problemsWhere there is more than one thing wrong, each of them.

Some errors carry more: allowed on a 405, limit where a call takes only so many, resendAfter on a code asked for too soon.

Types

TypeStatusDo
invalid_request400, 404, 405, 413, 422Change the request. The message says what to send instead. Retry: not as it is.
authentication401Send a valid key. Retry: not with the same key.
permission403Use a key that may make this call. Retry: not with the same key.
conflict409Read the record again, or use a new idempotency key. Retry: after reading again.
rate_limit429Slow down. Retry: after Retry-After seconds.
api_error500Retry once. If it fails again, quote the request_id. Retry: once.
unavailable502, 503, 504Something this call needs did not answer. Retry: after Retry-After seconds, or with backoff.

A product’s own refusal

Some products answer a request they read but cannot act on in their own words: "ok": false and a list of problems — a ledger with an event it cannot place, a credit file missing a field. That answer carries the envelope too: error is refused, message is its first problem, and param names the field where the product knows it. Its own fields are kept beside them.

curl -s -X POST https://app.creditcresttechnologies.com.au/v1/payments/debits \
  -H "authorization: Bearer $CREDITCREST_KEY" \
  -H 'content-type: application/json' \
  -d '{"accounts": []}'
const res = await fetch('https://app.creditcresttechnologies.com.au/v1/payments/debits', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.CREDITCREST_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    "accounts": []
  }),
});
const answer = await res.json();
if (!res.ok) throw new Error(`${answer.type} ${answer.error}: ${answer.message} Request ${answer.request_id}`);
console.log(answer);
import json, os, urllib.error, urllib.request

req = urllib.request.Request(
    "https://app.creditcresttechnologies.com.au/v1/payments/debits",
    data=json.dumps({
        "accounts": [],
    }).encode(),
    headers={
        "Authorization": "Bearer " + os.environ["CREDITCREST_KEY"],
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req) as res:
        print(json.load(res))
except urllib.error.HTTPError as e:
    err = json.load(e)
    print(err["type"], err["error"], err["message"], "Request", err["request_id"])
Response · 422trimmed
{
  "error": "refused",
  "type": "invalid_request",
  "message": "The day to debit must be YYYY-MM-DD.",
  "doc_url": "https://app.creditcresttechnologies.com.au/developers/errors#refused",
  "ok": false,
  "problems": ["The day to debit must be YYYY-MM-DD."],
  "request_id": "req_7c1e2a9f04b3d568"
}

Request IDs

Every answer, success or not, has a Request-Id header. Log it beside your own reference for the call. When you write to hello@creditcresttechnologies.com.au about a call, quote it: it is how that one call is found.

Retrying safely

An Idempotency-Key is 8 to 100 letters, digits, hyphens or underscores, such as a UUID. Sent again with the same request within 24 hours, the call runs again, its answer says Idempotent-Replayed: true, and it is counted and charged once.

curl -s -i -X POST https://app.creditcresttechnologies.com.au/v1/statement \
  -H "authorization: Bearer $CREDITCREST_KEY" \
  -H 'content-type: text/csv' \
  -H 'idempotency-key: read-2026-10-03-0001' \
  --data-binary @statement.csv
import { readFile } from 'node:fs/promises';

const res = await fetch('https://app.creditcresttechnologies.com.au/v1/statement', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.CREDITCREST_KEY}`,
    'content-type': 'text/csv',
    'idempotency-key': 'read-2026-10-03-0001',
  },
  body: await readFile('statement.csv'),
});
console.log(res.status, res.headers.get('idempotent-replayed') ?? 'first time');
import json, os, urllib.error, urllib.request

req = urllib.request.Request(
    "https://app.creditcresttechnologies.com.au/v1/statement",
    data=open("statement.csv", "rb").read(),
    headers={
        "Authorization": "Bearer " + os.environ["CREDITCREST_KEY"],
        "Content-Type": "text/csv",
        "Idempotency-Key": "read-2026-10-03-0001",
    },
    method="POST",
)
try:
    with urllib.request.urlopen(req) as res:
        print(res.status, res.headers.get("Idempotent-Replayed") or "first time")
except urllib.error.HTTPError as e:
    print(json.load(e))

Every code

Keys

CodeStatusMeansDo
key_required401The call needs a key and none was sent.Send "Authorization: Bearer <key>". A sandbox key is one call: POST /v1/keys/sandbox.
invalid_key401The key was not issued by Creditcrest, was altered, or is not whole.Copy the key again, whole. Keys begin cc_test_ or cc_live_.
expired_key401The key is past its expiry date.A sandbox key: get another. A live key: ask Creditcrest for a new one.
revoked_key401The key was withdrawn.Ask Creditcrest for a new key.
out_of_scope403The key is not for this product.The message lists the products the key covers. Ask for a key that covers this one.
wrong_lender403The key is another lender’s than the one in the address.Use the lender’s own key. GET /v1/keys/me says whose a key is.
test_key403A test key cannot read or change a lender’s real records.Use the lender’s live key.
live_key409A live key was sent to a lender whose records are not live yet.Use a test key until the lender is live.
sandbox_only422The lender is not keeping real records yet, so a live key is refused.Use a test key until the lender is live.
rate_limited429Too many calls this minute on this key, too many sandbox keys from one network address, or too many codes asked for from one network address.Wait the Retry-After seconds, then spread the calls out. Limits are on the Authentication page.
keys_unavailable503Keys cannot be checked or issued here just now.Retry shortly. If it persists, write to support with the request_id.
unauthorised401A provider’s webhook whose signature did not verify, or whose time was more than five minutes out.For the identity provider: check its signing secret and its clock.

The request

CodeStatusMeansDo
bad_request400The request could not be read: a field is missing or malformed, or the body is not what the endpoint takes.The message names the field and the shape to send. The endpoint’s reference lists what it takes.
not_found404No such address, or nothing is kept under that reference.GET /v1 lists every endpoint. Check the reference.
method_not_allowed405The address exists, but not for this method.Use a method listed in allowed (and in the Allow header).
too_large413The body is over the size this endpoint reads.Send less in one call. The message says the limit.
too_many413More items than one call takes: events, applications or transactions.Split them across calls. The message, and limit where given, say how many one call takes.
bad_day422"on" is not a day.Send YYYY-MM-DD.
bad_period422"from" or "to" is not a day, or "from" is after "to".Send both as YYYY-MM-DD, "from" first.
period_too_long422The period is longer than one report covers.Ask for a shorter period. The message says the longest.
bad_month400month is not a calendar month.Send month=YYYY-MM.
no_applications422The body has no applications.The message shows the shape of one.
no_comparisons422The body has no comparisons.Send the comparisons POST /v1/shadow returned.
refused422, 429The request was read, and what it asks cannot be done: problems, or the message, says why. A product’s own answer keeps its fields beside it, such as ok: false. As a 429, a code was asked for again too soon.Correct what problems lists; param names the field where it is known. As a 429, wait until resendAfter.
invalid422The application has problems against the lender’s own checks.Correct each item in problems and send it again.
too_many_codes429This mobile has been sent five codes today, or three in the last ten minutes, whoever asked. Nothing was sent or charged.Wait the Retry-After seconds, or prove the email address instead.
texts_held429The lender’s texts for today have reached its daily ceiling, so this one was held: not sent, not charged. Creditcrest is alerted.Use email codes until midnight in Sydney, or ask Creditcrest to raise the ceiling.

Retries

CodeStatusMeansDo
bad_idempotency_key400The Idempotency-Key header is not 8 to 100 letters, digits, hyphens or underscores.Send a random id, such as a UUID.
idempotency_conflict409The Idempotency-Key was used before for a different request.Use a new key for a new request. Reuse a key only to retry the same request.
conflict409Something else was recorded on the same loan at the same moment.Read the loan again, then retry.

Products

CodeStatusMeansDo
policy_unreadable422The credit policy text has problems.problems lists each line and what is wrong with it.
policy_cannot_run422The policy was read but cannot run on this application.The message says which rule and why.
not_evidence422The body is not signing evidence Creditcrest issued.Send the evidence exactly as it was returned.
not_valid422The evidence’s seal does not check, so no certificate is made.verification says what failed. Send the evidence unaltered.
not_signed404There is no signature on file for this loan.Check the loan reference, or have the contract signed first.
bad_name422The staff member’s name cannot be used.Send their full name, as it should appear on every change they make.
bad_role422The role is not one the console has.The message lists the roles.
bad_email422The work email cannot be used.The message says why.
not_on_list404Nobody active on the lender’s staff list has that work email.Add them to the staff list first, or send a name and role.

The lender’s set-up

CodeStatusMeansDo
not_open503This lender is not taking applications yet.The lender finishes going live first.
shadow_mode409The lender’s configuration is in shadow, so nothing reaches an applicant.Turn shadow off to go live.
records_unreachable503The lender’s own records service did not answer. Nothing was run.Check the records service is up, then retry.
no_session_secret503This lender keeps real records and the server has no session secret for staff sign-in.Write to support with the request_id.
usage_unavailable503Usage cannot be read just now.Retry after Retry-After seconds.

Creditcrest’s side

CodeStatusMeansDo
internal_error500The request failed on Creditcrest’s side.Retry once. If it fails again, write to support with the request_id.
Creditcrest’s own: 18 codes from the invoicing addresses, which take Creditcrest’s admin token
CodeStatusMeansDo
admin_token_required401An invoicing address needs Creditcrest’s admin token.Not for lenders.
not_configured503The invoice run is off on this server.Set USAGE_ADMIN_TOKEN.
bad_lender400lender is not a lender id.Send a lender id.
bad_format400format is not json or pdf.Send format=json or format=pdf.
bad_issued400issued is not a day the invoice can be dated.The message says why.
bad_adjustment400adjustment is not the number of a note.Send 1, 2, 3 …
bad_body400The adjustment’s reason is missing or too long.The message says the limit.
bad_rows400The rows to put back are not as usageNotKept logged them.Send {"rows": [...]} from the log.
already_issued409The invoice is issued and is not dated again.Ask without issued.
issued_meanwhile409Another note was issued at the same time.Look at the month again before issuing another.
not_issued404, 409No invoice was issued for that month.GET the invoice to issue it.
not_kept503A final invoice or note could not be kept, so it is not shown.Retry after Retry-After seconds.
nothing_to_adjust409Made again, the month agrees with the invoice as issued.Nothing to do.
remake_not_final409Made again, the month is not final.Fix what problems lists first.
issued_invoice_differs409Made again, the month differs from the invoice as issued.Issue an adjustment note.
not_as_issued500An issued invoice no longer reads back as it was issued.The message says what differs. Do not issue another until it is understood.
invoice_unavailable503The lender’s configuration, usage or issued invoices could not be read.Retry shortly.
gst_status_differs409The invoice was issued under a different GST status than the month has now.The message says what is owed.