Client portal

Embedded borrower self-service APIs.

Partner-authenticated APIs for embedding borrower self-service: profile, loans, repayments, savings, and documents, without staff JWT sessions.

Base URL

https://{host}/api/v1/{tenant_slug}/partner/v1

Header on every call:

X-API-Key: clfy_test_xxxxxxxx

Scopes: Portal write flows need * or scopes that include portal/client/loan access. A read-only key (e.g. loans:read only) will return 403 on POST /portal/loans/apply.

Borrower scope

Portal routes act on a specific borrower. In live mode pass either:

X-Client-Id: {client_uuid}

or ?client_id={client_uuid}.

In test mode the API uses deterministic mock borrower fixtures (Ada Okello); X-Client-Id is optional.

Core flows

Profile

curl -sS -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  "$BASE/portal/profile"

Apply for a loan

With a test key, use sandbox product UUID 00000000-0000-4000-8000-000000000030.
With a live key, use a real loan product ID from your tenant (must be portal-visible if your tenant restricts portal products).

curl -sS -X POST -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "00000000-0000-4000-8000-000000000030",
    "applied_amount": 5000,
    "term_days": 90,
    "purpose": "Working capital"
  }' \
  "$BASE/portal/loans/apply"

Tenants with required loan custom fields may also need custom_field_values in the body.

My loans, repayments, savings, documents

curl -sS -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  "$BASE/portal/loans"

curl -sS -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  "$BASE/portal/repayments"

curl -sS -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  "$BASE/portal/savings"

curl -sS -H "X-API-Key: $API_KEY" -H "X-Client-Id: $CLIENT_ID" \
  "$BASE/portal/documents"

Repayments are read-only on the portal (GET /portal/repayments). Record repayments via POST /repayments on the platform API (staff workflow must disburse the loan first on live keys).

Postman

Import external/postman/02-client-portal.postman_collection.json and set baseUrl, tenantSlug, apiKey, productId, and (for live) clientId.

Integration lab

Run make partner-lab (external/sample-apps/) against a local tenant to exercise portal reads + apply with bootstrap keys that have * scopes.

Related