Fragment is the Ledger API for engineers that move money. Stop wrangling payment tables, debugging balance errors and hacking together data pipelines. Start shipping the features that make a difference.
Using npm:
npm install @fragment-dev/node-clientUsing yarn:
yarn add @fragment-dev/node-clientTo instantiate a client, call createFragmentClient. You can generate credentials using the Fragment dashboard.
import{createFragmentClient}from"@fragment-dev/node-client";constfragment=createFragmentClient({params: {apiUrl: "api url from dashboard",clientId: "client id from dashboard",clientSecret: "client secret from dashboard",scope: "scope from dashboard",authUrl: "auth url from dashboard",},});Read the Using custom queries section to learn how to use your own GraphQL queries with the SDK.
To post a Ledger Entry defined in your schema:
awaitfragment.addLedgerEntry({ik: "some-ik",ledgerIk: "your-ledger-ik",type: "user_funds_account",posted: "1968-01-01T16:45:00Z",parameters: {user_id: "user-1",funding_amount: "200",},});To post a Ledger Entry with lines defined at runtime:
awaitfragment.addLedgerEntryRuntime({ik: "some-ik",ledgerIk: "your-ledger-ik",posted: "1968-01-01T16:45:00Z",type: "funding_runtime",lines: [{account: {path: "assets/banks/user-cash"},key: "funds_arrive_in_bank",amount: "100",},{account: {path: "liabilities/users:user-1/available"},key: "increase_user_balance",amount: "100",},],tags: [{key: "service",value: "funding-service"}],groups: [{key: "user",value: "user-1"},{key: "workflow",value: "wf-123"},],});When your Schema defines Ledger Entry types with lines, the SDK generates type-specific methods for posting those entries. These methods follow the naming convention Post<EntryType> where the entry type name is converted to PascalCase.
For example, an entry type user-funds-account generates a method PostUserFundsAccount:
awaitfragment.PostUserFundsAccount({ik: "some-ik",ledgerIk: "your-ledger-ik",amount: "200",});The SDK handles different naming conventions:
- Hyphenated:
user-funds-account→PostUserFundsAccount - CamelCase:
fundingSettlement→PostFundingSettlement - Underscore:
payment_processing→PostPaymentProcessing
When your schema defines multiple versions of the same entry type using typeVersion, the SDK generates separate methods for each version. Version 1 uses the base method name, and subsequent versions append _v2, _v3, etc.
For example, an entry type user-funds-account with typeVersion: 1 and typeVersion: 2 generates:
PostUserFundsAccountfor version 1PostUserFundsAccount_v2for version 2
// Post version 1 entryawaitfragment.PostUserFundsAccount({ik: "entry-ik-1",ledgerIk: "your-ledger-ik",amount: "200",});// Post version 2 entry (with additional feeAmount parameter)awaitfragment.PostUserFundsAccount_v2({ik: "entry-ik-2",ledgerIk: "your-ledger-ik",amount: "200",feeAmount: "10",});Each version can have different parameters and line structures, and the generated methods will reflect those differences.
To post a batch of Ledger Entries atomically:
import{userFundsAccountV1}from'./src/fragment-client.ts';awaitfragment.addLedgerEntries({entries: [userFundsAccountV1({ik: "some-ik-1",ledgerIk: "your-ledger-ik",posted: "1968-01-01T16:45:00Z",user_id: "user-1",funding_amount: "20000",}),userFundsAccountV1({ik: "some-ik-2",ledgerIk: "your-ledger-ik",posted: "1968-01-01T16:45:00Z",user_id: "user-2",funding_amount: "20000",}),],});Construct the entries in the batch using the typed payloads generated for your Schema, named <entryType>V<typeVersion>. They are exported from your generated client alongside getSdk.
To sync transaction using a Custom Link:
import{CurrencyCode}from"@fragment-dev/node-client";awaitfragment.syncCustomAccounts({linkId: "custom-link-id",accounts: [{externalId: "operating-account",name: "Operating Bank Account",currency: {code: CurrencyCode.Usd,},},],});awaitfragment.syncCustomTxs({linkId: "custom-link-id",txs: [{externalId: "tx-123",description: "Test user funding",account: {externalId: "operating-account",linkId: "custom-link-id",},amount: "100",currency: {code: CurrencyCode.Usd,},posted: "1968-01-01",},],});To reconcile a transaction:
awaitfragment.reconcileTx({ledgerIk: "your-ledger-ik",type: "funding_settlement",parameters: {user_id: "user-1",net_amount: "99",fee_amount: "1",link_id: "stripe",link_account_id: "stripe-balance",link_tx_id: "tx_456",},});To reconcile a Ledger Entry with lines defined at runtime:
awaitfragment.reconcileTxRuntime({ledgerIk: "your-ledger-ik",type: "funding_settlement_runtime",lines: [{key: "funds_arrive_at_stripe",account: {path: "assets/banks/stripe"},amount: "100",tx: {externalId: "tx_456",},},{key: "increase_user_balance",account: {path: "liabilities/users:user-1/available"},amount: "100",},],tags: [{key: "service",value: "funding-service"}],groups: [{key: "user",value: "user-1"},{key: "workflow",value: "wf-123"},],});const{ schema }=awaitfragment.getSchema({key: schemaKey,});const{ ledger }=awaitfragment.getLedger({ik: "your-ledger-ik",});const{ ledgerEntry }=awaitfragment.getLedgerEntry({ik: "card_swipe_a",ledgerIk: "your-ledger-ik",});To get a Ledger Account with its balances:
const{ ledgerAccount }=awaitfragment.getLedgerAccountBalance({ledgerIk: "your-ledger-ik",path: "assets/receivables/user:user-1",});To get a Ledger Account with its lines:
const{ ledgerAccount }=awaitfragment.getLedgerAccountLines({ledgerIk: "your-ledger-ik",path: "assets/receivables/user:user-1",});To retrieve the Ledger Accounts in a Ledger:
constresult=awaitfragment.listLedgerAccounts({ledgerIk: "your-ledger-ik",});assert(result.ledger?.ledgerAccounts?.nodes,"Failed to list ledger accounts");To retrieve Ledger Accounts with balances:
constresult=awaitfragment.listLedgerAccountBalances({ledgerIk: "your-ledger-ik",});assert(result.ledger?.ledgerAccounts?.nodes,"Failed to list ledger accounts");To retrieve multi-currency Ledger Accounts with balances:
constresult=awaitfragment.listMultiCurrencyLedgerAccountBalances({ledgerIk: "your-ledger-ik",});assert(result.ledger?.ledgerAccounts?.nodes,"Failed to list ledger accounts");While the SDK comes with GraphQL queries out of the box, you may want to customize these queries for your product. In order to do that:
- Define your custom GraphQL queries in a GraphQL file. For example, in
custom-queries.graphql:
mutationgetSchemaName($key: SafeString!) {
schema(schema: { key: $key }) {
keyname
}
}- Run
fragment-node-client-codegento generate the client for your custom queries.
Using npx:
npx fragment-node-client-codegen -i custom-queries.graphql -o src/fragment-client.tsUsing yarn:
yarn fragment-node-client-codegen -i custom-queries.graphql -o src/fragment-client.ts- Pass the
getSdkfunction from the generated file tocreateFragmentClientto use your custom query!
import{createFragmentClient}from"@fragment-dev/node-client";import{getSdk}from'./src/fragment-client.ts';constfragment=createFragmentClient({params: {apiUrl: "api url from dashboard",clientId: "client id from dashboard",clientSecret: "client secret from dashboard",scope: "scope from dashboard",authUrl: "auth url from dashboard",},
getSdk,});// The returned client includes the pre-defined queries as well.awaitfragment.storeSchema({ ... });awaitfragment.getSchemaName({key: "a-schema-key"});