FH
FinHero Adapter: Trade Credit
TRADE-CREDIT INTEGRATION PLAYBOOK

Designing APIs for B2B Alternative Credit Scoring

As an ERP, invoicing platform, or trade-credit ledger provider, your system carries real-time accounts receivable and payable history. This playbook helps your team create secure lookup APIs and write adapters conformant with FinHero's B2B underwriting standards.

Product Manager Focus

Expose AR/AP aging buckets, debtor concentrations, and cash conversion cycles to support B2B loan assessments.

  • KYC Key: Map business entities using Malaysian registration IC keys.
  • Ledger summaries: Structure revenue streams and credit defaults.
  • Value Proposition: Empower corporate customers to leverage ledger history for capital facilities.

API Engineering Focus

Implement the Javascript adapter querying the accounting ledger API and mapping outputs to schema fields.

  • Fetch Method: Establish secure token calls fetching general ledger details.
  • Extract Method: Compute Days Sales Outstanding (DSO) and margin percentages.
  • Correctness: Ensure values represent correct units and expected bounds.

Integration & QA Focus

Validate the adapter mapping scripts offline using schema checkers and sandbox test files.

  • Validation: Schema-check manifest structures with CLI validation commands.
  • Reliable: Write expected input-output fixtures to guarantee math alignment.
  • Containers: Run isolated Compose services mapping mock ledger credentials.
๐Ÿ“ˆ

Understanding the "trade-credit" Category

The B2B scoring model utilizes Days Sales Outstanding (DSO), debtor concentration top-5 ratios, and cash conversion cycles. This provides a direct assessment of supplier payment hygiene.

๐Ÿ“Š

Data Extraction Pipeline

How business identities map to trade ledger metrics. The process runs locally within the secure FinSys execution stack.

1. FinHero Host Receives B2B Application Identity: { IC, Name } 2. Adapter: fetch() Query Ledger API POST /accounts/lookup 3. Adapter: extract() Compute aging details DSO & Margin calculations 4. Scoring Ingestion Canonical DB Storage ihs_alt_data_trade_credit
STEP 01
Trigger Application
FinHero identifies a B2B financing application and passes company registration IC credentials.
STEP 02
API Lookup Request
The adapter executes `fetch()`, calling your ERP accounting portal to export current ledger metrics.
STEP 03
Adapter Extract
The adapter's `extract()` method normalizes current AR aging, defaults, and cash cycles.
STEP 04
Database Storage
Normalized fields are stored inside `ihs_alt_data_trade_credit` for credit scoring.
๐Ÿ“‹

Canonical Trade Credit Fields

Your API doesn't need to match this structure directly. The adapter maps ledger outputs into these 10 canonical fields.

Field Key Data Type Constraint / Range Credit Scoring Interpretation
arDaysSalesOutstanding number days [0 - 400] Days Sales Outstanding โ€” average days taken to collect receivables. Lower indicates efficiency.
apDaysPayableOutstanding number days [0 - 400] Days Payable Outstanding โ€” average days taken to pay suppliers. Very high suggests cash flow strain.
arTotalOutstanding number MYR [0.0 - 1e9] Total accounts receivable balance outstanding. Shows the size of the debtor book.
arCurrentRatio number ratio [0.0 - 1.0] Fraction of the receivables book that is current. Closer to 1.0 is healthier.
arOverdue90PlusRatio number ratio [0.0 - 1.0] Fraction of receivables aged 90+ days overdue. Flag for bad-debt risk.
debtorConcentrationTop5Ratio number ratio [0.0 - 1.0] Concentration share of top 5 debtors. Above 0.60 signals customer risk.
tradeReferenceDefaults12m number count [0 - 1000] Supplier-reported payment defaults or returned payments. Direct distress indicator.
accountingRevenue12m number MYR [0.0 - 1e9] Trailing 12-month turnover per general ledger. Cross-referenced against bank statements.
grossMarginPct number ratio [0.0 - 1.0] Gross margin (gross profit / revenue). Predicts profitability quality.
cashConversionCycleDays number days [-200 - 600] Cash Conversion Cycle. Lower is more capital efficient.
๐Ÿงช

API Mapping Playground

Select a B2B applicant persona, or customize the values in the control panel to see live mapping calculations for trade ledger outputs.

Inputs Controls interactive
LEDGER_API_PAYLOAD.JSON Upstream Raw
โž”
extract()
CANONICAL_EXTRACTION.JSON Adapter Output
๐Ÿ’ป

Source Adapter Implementation

Complete template files for the Trade Credit mapping adapter. Implement `fetch` and `extract` methods conformant to the `trade-credit` category schema.

// manifest.json: Declares entrypoint & outputs metadata
{
  "manifestVersion": 1,
  "id": "trade-credit-adapter-v1",
  "displayName": "B2B Ledger Ingestion",
  "category": "trade-credit",
  "version": 1,
  "produces": [
    "arDaysSalesOutstanding",
    "apDaysPayableOutstanding",
    "arTotalOutstanding",
    "arCurrentRatio",
    "arOverdue90PlusRatio",
    "debtorConcentrationTop5Ratio",
    "tradeReferenceDefaults12m",
    "accountingRevenue12m",
    "grossMarginPct",
    "cashConversionCycleDays"
  ],
  "requiredIdentityFields": [],
  "implementation": {
    "type": "typescript",
    "entryPoint": "extract.mjs"
  }
}
// extract.mjs: Queries API, transforms metrics to canonical fields
const API_URL = process.env.FAKE_TRADE_CREDIT_API_URL ?? "http://fake-trade-credit-api:4300";
const API_KEY = process.env.FAKE_TRADE_CREDIT_API_KEY ?? "demo-key";

const round4 = (n) => Number(n.toFixed(4));
const ratio = (num, den) => (den > 0 ? round4(num / den) : 0);

const adapter = {
  id: "trade-credit-adapter-v1",
  category: "trade-credit",
  version: 1,
  produces: [
    "arDaysSalesOutstanding",
    "apDaysPayableOutstanding",
    "arTotalOutstanding",
    "arCurrentRatio",
    "arOverdue90PlusRatio",
    "debtorConcentrationTop5Ratio",
    "tradeReferenceDefaults12m",
    "accountingRevenue12m",
    "grossMarginPct",
    "cashConversionCycleDays"
  ],

  async fetch(identity) {
    if (!identity?.ic || !identity?.fullName) {
      throw new Error("fake-trade-credit-v1: identity.ic and identity.fullName are required");
    }
    const res = await fetch(`${API_URL}/v1/accounts/lookup`, {
      method: "POST",
      headers: { "content-type": "application/json", "x-api-key": API_KEY },
      body: JSON.stringify({ ic: identity.ic, fullName: identity.fullName })
    });
    if (!res.ok) {
      throw new Error(`fake-trade-credit-v1 fetch failed: HTTP ${res.status}`);
    }
    return res.json();
  },

  async extract(raw) {
    const ar = raw.accountsReceivable ?? {};
    const aging = ar.aging ?? {};
    const total = Number(ar.totalMyr ?? 0);
    const pnl = raw.incomeStatement12m ?? {};
    const revenue = Number(pnl.revenueMyr ?? 0);
    const cogs = Number(pnl.cogsMyr ?? 0);

    return [{
      instanceKey: "default",
      observedAt: new Date().toISOString(),
      values: {
        arDaysSalesOutstanding: Number(raw.metrics?.daysSalesOutstanding ?? 0),
        apDaysPayableOutstanding: Number(raw.accountsPayable?.avgDaysOutstanding ?? 0),
        arTotalOutstanding: total,
        arCurrentRatio: ratio(Number(aging.current ?? 0), total),
        arOverdue90PlusRatio: ratio(Number(aging.d90plus ?? 0), total),
        debtorConcentrationTop5Ratio: round4(Number(ar.top5DebtorShareRatio ?? 0)),
        tradeReferenceDefaults12m: Number(raw.tradeReferences?.defaults12m ?? 0),
        accountingRevenue12m: revenue,
        grossMarginPct: ratio(revenue - cogs, revenue),
        cashConversionCycleDays: Number(raw.metrics?.cashConversionCycleDays ?? 0)
      }
    }];
  }
};

export default adapter;
// test.mjs: Offline verification tests
import { runFixtures } from "@finsys/adapter-toolkit";
import adapter from "./extract.mjs";

const fixtures = [
  {
    name: "aiman-hassan-ledger",
    identity: { ihsId: 1, ic: "850101015432", fullName: "Aiman bin Hassan" },
    expected: [
      {
        instanceKey: "default",
        values: {
          arDaysSalesOutstanding: 28,
          accountingRevenue12m: 2400000
        }
      }
    ]
  }
];

const results = await runFixtures(adapter, fixtures);
console.log(results);
if (results.some(r => !r.ok)) {
  console.error("โŒ Trade Credit tests failed");
  process.exit(1);
} else {
  console.log("โœ… Trade Credit validation tests passed!");
}
๐Ÿ› ๏ธ

Testing & Validation Run Commands

Validate manifest schemas and test mapping logic locally using the CLI or docker configurations.

bash โ€” manifest validation
$ npx finsys-adapter-toolkit validate ./my-trade-credit-adapter
โœ“ Parsing manifest.json schema... โœ“ Resolved entry point extract.mjs โœ“ Validated categories mapping matches 'trade-credit' fields... โœ… Validation passed successfully!
bash โ€” run offline fixtures
$ node ./my-trade-credit-adapter/test.mjs
Running 1 fixture checks against adapter... [Fixture: aiman-hassan-ledger] - DSO check: 28 - OK - Revenue check: 2400000 - OK โœ… All tests passed.
docker โ€” local sandbox deployment
$ docker compose -f examples/fake-trade-credit/docker-compose.yml up --build -d
Building fake-trade-credit-api... Creating fake-trade-credit-api-container ... done Attaching API endpoint to localhost:4300