@borrowbetter/swsdk
v0.3.1
Published
Spinwheel credit data and debt profile SDK
Readme
@borrowbetter/swsdk
Internal use only. This package is maintained by BorrowBetter for internal projects. It is published publicly for convenience but is not intended for general use. No support or stability guarantees are provided for external consumers.
TypeScript SDK for the Spinwheel credit data and debt profile API. Provides type-safe access to user connection, debt profile fetching, and hydrated user data.
Installation
npm install @borrowbetter/swsdkRequirements
- Node.js >= 18
- Spinwheel API key (provided by your Spinwheel account manager)
Quick Start
import { SpinwheelSDK } from "@borrowbetter/swsdk";
const spinwheel = new SpinwheelSDK({
apiKey: process.env.SPINWHEEL_API_KEY,
sandbox: process.env.NODE_ENV !== "production",
});API
Connect
Connect a user to Spinwheel via preverified phone or network token.
// Preverified phone (requires Spinwheel support approval)
const result = await spinwheel.connect.preverifiedPhone({
phoneNumber: "+14155552671",
dateOfBirth: "1990-01-15",
extUserId: "your-internal-user-id",
audit: {
verificationDate: "2025-01-01",
userConsentDate: "2025-01-01",
verificationType: "SMS",
},
});
const { userId } = result.data;
// Network token
const result = await spinwheel.connect.networkToken({
extUserId: "your-internal-user-id",
networkToken: "token-from-verification",
ssnLastFourDigits: "1234",
dateOfBirth: "1990-01-15",
audit: { userConsentDate: "2025-01-01" },
});networkToken also accepts include, which returns a snapshot of the user's already existing
credit report alongside the connect response, saving a userManagement.get round trip:
const { data } = await spinwheel.connect.networkToken({
extUserId: "your-internal-user-id",
networkToken: "token-from-verification",
ssnLastFourDigits: "1234",
dateOfBirth: "1990-01-15",
audit: { userConsentDate: "2025-01-01" },
include: ["CREDIT_REPORT"],
});
if (data.dataStatus?.status === "SUCCESS") {
data.creditReports;
data.creditCards;
data.creditCardSummary; // ...and the other liabilities and summaries
}The connect itself succeeds independently of the snapshot — check dataStatus to see whether
the credit data came back:
| statusCode | Meaning |
|--------------|---------|
| 2000 | Success — credit reports, liabilities, and summaries returned |
| 4040 | Connected, but no existing credit report was found |
| 4030 | Connected, but no data returned — account not configured for this |
| 5000 | Connected, but credit report retrieval failed |
Only
CREDIT_REPORTis supported, and an empty array is rejected with a 400 — omit the field entirely if you don't want the extra data. This returns an existing report only; it does not trigger a pull, sodebtProfile.fetchis still the way to source one (a user with no report yet comes back4040).
Debt Profile
Trigger an asynchronous credit pull. Results are retrieved via userManagement.get() once processing completes.
await spinwheel.debtProfile.fetch(userId, {
creditReport: {
sourceBureau: "TransUnion",
type: "1_BUREAU.FULL",
},
creditScore: {
sourceBureau: "TransUnion",
model: "VANTAGE_SCORE_3_0",
},
});Note: Your implementation must display the Spinwheel End User Agreement before calling this method.
User Management
Retrieve a user's full hydrated profile including all liabilities and credit reports. Always uses the secure endpoint with unmasked account number and SSN.
// By Spinwheel user ID
const { data } = await spinwheel.userManagement.get({ userId });
// By your external user ID
const { data } = await spinwheel.userManagement.get({ extUserId: "your-id" });
// Available on the hydrated user
data.profile?.creditScore;
data.creditCardSummary;
data.personalLoanSummary;
data.autoLoanSummary;
data.homeLoanSummary;
data.studentLoanSummary;
data.creditReports;
data.creditCards;
data.personalLoans;
data.autoLoans;
data.homeLoans;
data.studentLoans;
data.miscellaneousLiabilities;Configuration
new SpinwheelSDK({
apiKey: string; // Required — your Spinwheel API key
sandbox?: boolean; // Optional — defaults to false (production)
retry?: number | RetryOptions; // Optional — default ky retry for every request; overridable per call
});Retry
Retry is ky's — pass a count or a RetryOptions object. Set a client-wide default via the constructor, and override it per request by passing an options object as the last argument to any method:
const spinwheel = new SpinwheelSDK({
apiKey: "your-api-key",
retry: { limit: 3 }, // default for all requests
});
// Override for a single call — e.g. poll the profile harder:
await spinwheel.userManagement.get({ userId }, { retry: { limit: 5 } });
// Disable retries for one call:
await spinwheel.userManagement.get({ userId }, { retry: 0 });Note: ky only retries idempotent methods (GET/PUT/HEAD/DELETE/OPTIONS/TRACE) by default. Retry applies out of the box to
userManagement.get(a GET). The POST-based methods (connect.*,debtProfile.fetch) will not retry unless you opt in withretry: { methods: ["post"] }— weigh the double-submit risk before doing so.
Response Shape
All methods return Spinwheel's standard response envelope:
interface SpinwheelResponse<T> {
status: {
code: number;
desc: string;
messages?: Array<{ desc: string; type?: "ERROR" | "WARN" | "INFO" }>;
};
data: T;
}const result = await spinwheel.connect.preverifiedPhone({ ... });
if (result.status.code >= 400) {
console.error(result.status.messages);
return;
}
const { userId } = result.data;Type Exports
All types are exported directly from the package:
import type {
SpinwheelConfig,
SpinwheelRequestOptions,
HydratedUserData,
UserData,
CreditReport,
CreditCard,
PersonalLoan,
AutoLoan,
HomeLoan,
StudentLoan,
LiabilityType,
CreditBureau,
// ...and more
} from "@borrowbetter/swsdk";Development
npm install
npm run dev # tsup watch mode
npm run build # compile to dist/
npm run typecheck # TypeScript check
npm run lint # Biome lint
npm run format # Biome format + auto-fix
npm run smoke # Run smoke test against sandboxSmoke Test
cp .env.example .env.local
# Add SPINWHEEL_API_KEY to .env.local
npm run smokeScripts
| Script | Description |
|--------|-------------|
| npm run build | Compile to dist/ |
| npm run dev | Watch mode |
| npm run smoke | End-to-end test against sandbox |
| npm run lint | Biome lint check |
| npm run format | Biome format + auto-fix |
| npm run typecheck | TypeScript type check |
