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).
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.
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.
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
| Key | Where | Notes |
|---|---|---|
| sk_sandbox_… | Public sandbox | Shared test key on this page. Read-only, rate-limited, sandbox data only. |
| sk_live_… | Your institution | Issued per institution after onboarding. Scoped to your consent agreements and volume tier. Request one → |
POST https://pglbndkxmflyfxpuhaaf.supabase.co/functions/v1/credit-queryOnly 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.
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.
| Field | Type | Description |
|---|---|---|
| subject_refrequired | string | Pseudonymous subject id. Pattern [A-Za-z0-9_-]{1,128}. Never a raw national ID or phone. |
| consent_tokenrequired | string | The consent grant authorising this query. Must be granted, unexpired, and bound to your institution, this subject, and the purpose. |
| purposerequired | enum | One of loan_origination or credit_assessment. The consent's scope must cover the sources this purpose requires. |
| idempotency_keyoptional | string | Replays the original scored response verbatim if you retry — no re-score, no double-count. See Idempotency. |
{
"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-…"
}| Field | Type | Description |
|---|---|---|
| query_id / audit_ref | uuid | Identifier of this query and its immutable audit row (same value). |
| status | enum | scored, building or provisional. See Statuses. |
| score | int · null | 300–850 when scored; null for provisional. |
| tier | string · null | Poor / Fair / Good / Very Good / Excellent. Omitted while building. |
| confidence | float | 0–1, two decimals — how much signal backed the score. |
| factors | object | positive and negative arrays of reason codes, each with EN + Amharic labels and an impact. See Reason codes. |
| recommendation | object | action, eligible (bool), and indicative_limit_etb when applicable. Advisory — the lender always keeps the decision. |
| data_sources | string[] | The consented scopes that fed this score. |
| segment | enum | urban, farmer or sme — the sub-model applied. |
| model_version / config_hash | string | The exact model and a tamper-evident hash of its frozen config, recorded on every audit row. |
| Status | Meaning | Fields |
|---|---|---|
| scored | Enough signal for a confident score. | score + tier + confidence. |
| building | Thin 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. |
| provisional | Identity 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.
| Tier | Range | Typical recommendation |
|---|---|---|
| Poor | 300–579 | decline_or_refer |
| Fair | 580–669 | review_or_secured_terms |
| Good | 670–739 | approve_with_conditions |
| Very Good | 740–799 | approve_standard_terms |
| Excellent | 800–850 | approve_standard_terms |
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.
| Code | English | Amharic | Category |
|---|---|---|---|
| PAY_ONTIME | Consistent on-time payment history | ወጥ የሆነ በወቅቱ የመክፈል ታሪክ | Payment |
| PAY_LOAN_ONTIME | Loans repaid on time | ብድሮች በወቅቱ ተከፍለዋል | Payment |
| PAY_MISSED_RECENT | Recently missed one or more payments | በቅርቡ አንድ ወይም ከዚያ በላይ ክፍያዎች አልተከፈሉም | Payment |
| THIN_HISTORY | Limited credit and financial history | ውስን የሆነ የብድርና የፋይናንስ ታሪክ | Payment |
| ACT_LOW_TENURE | Short account / activity history | አጭር የሂሳብ ወይም የእንቅስቃሴ ታሪክ | Activity |
| ACT_VOLATILE_BALANCE | Highly variable account balance | በጣም ተለዋዋጭ የሂሳብ ቀሪ መጠን | Activity |
| ACT_REGULAR_INFLOW | Regular, steady income inflows | መደበኛና የተረጋጋ የገቢ ፍሰት | Activity |
| SAV_STREAK | Sustained savings streak | ቀጣይነት ያለው የቁጠባ ልምድ | Savings |
| SAV_NONE | No savings activity detected | ምንም የቁጠባ እንቅስቃሴ አልተገኘም | Savings |
| ID_VERIFIED | Identity verified with Fayda | ማንነት በፋይዳ ተረጋግጧል | Identity |
| ID_UNVERIFIED | Identity not yet verified with Fayda | ማንነት እስካሁን በፋይዳ አልተረጋገጠም | Identity |
| OB_NOT_CONNECTED | Few connected financial accounts | ጥቂት የተገናኙ የፋይናንስ ሂሳቦች | Identity |
| ENG_EDUCATED | Completed financial-education modules | የፋይናንስ ትምህርት ክፍሎችን አጠናቅቋል | Engagement |
{ "error": { "code": "consent_required", "message": "Consent missing, expired, revoked, …" } }| Code | HTTP | When |
|---|---|---|
| invalid_request | 400 | Malformed JSON, unknown purpose, bad subject_ref, or a raw fayda_id/phone was sent. |
| unauthorized | 401 | Missing/invalid API key, or the institution is not active. No audit row is written. |
| consent_required | 403 | Consent missing, expired, revoked, not bound to this institution/subject, or outside granted scope. Audited as a denial. |
| subject_not_found | 404 | Unknown subject_ref or no feature vector on file. |
| method_not_allowed | 405 | Any method other than POST/OPTIONS. |
| thin_file_no_score | 422 | Insufficient history to produce a score. Audited as a denial. |
| rate_limited | 429 | Quota exceeded — includes a Retry-After header. Audited. |
| internal_error | 500 | Any DB/scoring failure or a failed audit write (we never serve a score we couldn't log). |
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.
| Tier | Per hour | Per day |
|---|---|---|
| Pilot (sandbox & early pilots) | 30 | 200 |
| Standard | 600 | 10,000 |
| Custom | Tuned to your portfolio volume — set during onboarding. | |
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
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.
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.
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.
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.
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.