Developer Docs

The Sheba Credit API.

One REST endpoint returns a consented, explainable, fully-audited credit score for any Sheba-registered Ethiopian consumer — in real time. This is the complete reference: authenticate, send a query, read the score. Built to comply with NBE Directive CRB/02/2019 (Sheba is pre-licence — building toward NBE licensing).

Quickstart

Score a subject in one call.

Everything below uses public sandbox credentials — a shared test institution key and a seeded, consented test subject. Safe to share, rate-limited, and scoped to the sandbox. Same scoring engine, audit log and response shape as production; only the data is seeded.

curl -sS -X POST \
  "https://pglbndkxmflyfxpuhaaf.supabase.co/functions/v1/credit-query" \
  -H "Authorization: Bearer sk_sandbox_sheba_pilot_7f3a9c2e1b8d4056" \
  -H "Content-Type: application/json" \
  -d '{
    "subject_ref": "sub_9f3a",
    "consent_token": "cnst_sandbox_3b1e7d92",
    "purpose": "loan_origination"
  }'

Returns 200 with a Sheba Score of 724 (Good), the factors that drove it, an eligibility recommendation, and an immutable audit_ref.

Try it live

Send a real request from your browser.

This calls the live sandbox endpoint directly — the same one your server would hit. Edit the fields and send. (In-browser calls are CORS-scoped to shebacredit.com and localhost; from anywhere else, use the curl above — it works from any server.)

Response will appear here.

Uses the public sandbox key. Rate-limited to 30 requests/hour. No data leaves the sandbox.

Authentication

Bearer API key.

Every request carries your institution's secret key in the Authorization header. Keys are hashed (SHA-256) at rest — Sheba never stores the raw key. Auth happens before any request body is parsed; a bad or inactive key fails closed with 401 and writes no audit row.

Authorization: Bearer sk_live_your_institution_key
Content-Type: application/json
KeyWhereNotes
sk_sandbox_…Public sandboxShared test key on this page. Read-only, rate-limited, sandbox data only.
sk_live_…Your institutionIssued per institution after onboarding. Scoped to your consent agreements and volume tier. Request one →
Endpoint

One endpoint. One method.

POST https://pglbndkxmflyfxpuhaaf.supabase.co/functions/v1/credit-query

Only POST is supported (a preflight OPTIONS is handled for browsers). Any other method returns 405 with an Allow: POST, OPTIONS header. Typical end-to-end latency is under 500 ms.

Request body

JSON fields.

Send a JSON body (max 16 KB). Sheba is data-minimising by design: raw fayda_id or phone are rejected — you supply the pseudonymous subject_ref only.

FieldTypeDescription
subject_refrequiredstringPseudonymous subject id. Pattern [A-Za-z0-9_-]{1,128}. Never a raw national ID or phone.
consent_tokenrequiredstringThe consent grant authorising this query. Must be granted, unexpired, and bound to your institution, this subject, and the purpose.
purposerequiredenumOne of loan_origination or credit_assessment. The consent's scope must cover the sources this purpose requires.
idempotency_keyoptionalstringReplays the original scored response verbatim if you retry — no re-score, no double-count. See Idempotency.
Response · 200

The scored response.

{
  "query_id": "a3f1c8e2-…",
  "subject_ref": "sub_9f3a",
  "status": "scored",
  "score": 724,
  "tier": "Good",
  "confidence": 0.95,
  "scale": [300, 850],
  "factors": {
    "positive": [
      { "code": "PAY_ONTIME", "label_en": "Consistent on-time payment history", "label_am": "ወጥ የሆነ በወቅቱ የመክፈል ታሪክ", "impact": "high" }
    ],
    "negative": []
  },
  "recommendation": { "action": "approve_with_conditions", "eligible": true, "indicative_limit_etb": 25000 },
  "data_sources": ["telebirr", "sheba_save", "fayda", "education"],
  "segment": "urban",
  "model_version": "sheba-score-v1.0.0",
  "config_hash": "…",
  "generated_at": "2026-07-01T10:42:07Z",
  "audit_ref": "9c21e7b4-…"
}
FieldTypeDescription
query_id / audit_refuuidIdentifier of this query and its immutable audit row (same value).
statusenumscored, building or provisional. See Statuses.
scoreint · null300–850 when scored; null for provisional.
tierstring · nullPoor / Fair / Good / Very Good / Excellent. Omitted while building.
confidencefloat0–1, two decimals — how much signal backed the score.
factorsobjectpositive and negative arrays of reason codes, each with EN + Amharic labels and an impact. See Reason codes.
recommendationobjectaction, eligible (bool), and indicative_limit_etb when applicable. Advisory — the lender always keeps the decision.
data_sourcesstring[]The consented scopes that fed this score.
segmentenumurban, farmer or sme — the sub-model applied.
model_version / config_hashstringThe exact model and a tamper-evident hash of its frozen config, recorded on every audit row.
Statuses

Not every file is scorable — and that's honest.

StatusMeaningFields
scoredEnough signal for a confident score.score + tier + confidence.
buildingThin file — real but limited history. The score is capped (≤700) and confidence is low, so you never over-trust a sparse file.score (capped), no tier.
provisionalIdentity not yet Fayda-verified — we won't emit a score without a verified identity.score = null, recommendation verify_identity.

A truly empty file returns 422 thin_file_no_score rather than a fabricated number.

Score tiers

300–850, five bands.

TierRangeTypical recommendation
Poor300–579decline_or_refer
Fair580–669review_or_secured_terms
Good670–739approve_with_conditions
Very Good740–799approve_standard_terms
Excellent800–850approve_standard_terms
Reason codes

Every score is explainable — in English and Amharic.

The code is the stable, language-independent contract; the labels are what you show a customer. Because NBE CRB/02/2019 requires local-language adverse-action reasons, every code carries both. Use the code in logic; render label_am on the decline notice.

CodeEnglishAmharicCategory
PAY_ONTIMEConsistent on-time payment historyወጥ የሆነ በወቅቱ የመክፈል ታሪክPayment
PAY_LOAN_ONTIMELoans repaid on timeብድሮች በወቅቱ ተከፍለዋልPayment
PAY_MISSED_RECENTRecently missed one or more paymentsበቅርቡ አንድ ወይም ከዚያ በላይ ክፍያዎች አልተከፈሉምPayment
THIN_HISTORYLimited credit and financial historyውስን የሆነ የብድርና የፋይናንስ ታሪክPayment
ACT_LOW_TENUREShort account / activity historyአጭር የሂሳብ ወይም የእንቅስቃሴ ታሪክActivity
ACT_VOLATILE_BALANCEHighly variable account balanceበጣም ተለዋዋጭ የሂሳብ ቀሪ መጠንActivity
ACT_REGULAR_INFLOWRegular, steady income inflowsመደበኛና የተረጋጋ የገቢ ፍሰትActivity
SAV_STREAKSustained savings streakቀጣይነት ያለው የቁጠባ ልምድSavings
SAV_NONENo savings activity detectedምንም የቁጠባ እንቅስቃሴ አልተገኘምSavings
ID_VERIFIEDIdentity verified with Faydaማንነት በፋይዳ ተረጋግጧልIdentity
ID_UNVERIFIEDIdentity not yet verified with Faydaማንነት እስካሁን በፋይዳ አልተረጋገጠምIdentity
OB_NOT_CONNECTEDFew connected financial accountsጥቂት የተገናኙ የፋይናንስ ሂሳቦችIdentity
ENG_EDUCATEDCompleted financial-education modulesየፋይናንስ ትምህርት ክፍሎችን አጠናቅቋልEngagement
Errors

Every failure is typed and fail-closed.

{ "error": { "code": "consent_required", "message": "Consent missing, expired, revoked, …" } }
CodeHTTPWhen
invalid_request400Malformed JSON, unknown purpose, bad subject_ref, or a raw fayda_id/phone was sent.
unauthorized401Missing/invalid API key, or the institution is not active. No audit row is written.
consent_required403Consent missing, expired, revoked, not bound to this institution/subject, or outside granted scope. Audited as a denial.
subject_not_found404Unknown subject_ref or no feature vector on file.
method_not_allowed405Any method other than POST/OPTIONS.
thin_file_no_score422Insufficient history to produce a score. Audited as a denial.
rate_limited429Quota exceeded — includes a Retry-After header. Audited.
internal_error500Any DB/scoring failure or a failed audit write (we never serve a score we couldn't log).
Rate limits

Per-institution quotas, fail-closed.

Counters are per institution + key, on fixed hour and day windows. Over the limit returns 429 with Retry-After (seconds) and a retry_after_seconds field. If the limiter itself can't be checked, the request is denied — a regulated score is never served unproven.

TierPer hourPer day
Pilot (sandbox & early pilots)30200
Standard60010,000
CustomTuned to your portfolio volume — set during onboarding.
Idempotency

Safe retries, no double-billing.

Include an idempotency_key in the body. If you retry the same key, Sheba replays the original scored response verbatim and returns an Idempotent-Replay: true header — no re-score, no re-count against your quota. Transient 429 denials are not bound to the key, so the same key can still succeed once quota frees up.

-d '{ "subject_ref": "sub_9f3a", "consent_token": "cnst_…", "purpose": "loan_origination", "idempotency_key": "loan-4821-attempt-1" }'

# retry with the same key → 200 + header Idempotent-Replay: true
Consent & audit

Consent-gated, minimised, and immutably logged.

Fail-closed consent

Every query is gated on a live, granted, unexpired consent bound to your institution, the subject, and the purpose. Revoke a data source's consent and it is excluded from the next score — per-source, not all-or-nothing.

Immutable audit trail

Every query — approval and denial — writes one append-only, hash-chained audit row. The chain is independently verifiable end-to-end. This is the NBE CRB/02/2019 pre-lending-query evidence, by construction.

Data minimisation

Raw national ID and phone are never accepted, logged, or returned — only the pseudonymous subject_ref. The scorer sees a verified-identity flag, never the identity itself.

Lender keeps the decision

Sheba returns a score and machine-readable reason codes. The credit decision — and the consumer-facing adverse-action notice — remain yours. Sheba is pre-licence, building toward NBE licensing.

Ready for a production key?

The sandbox uses a shared test key. To run real queries on consented consumers, request a dedicated institution key — we'll set up your institution, scope your consent, and tune your volume tier.