Payments made by your product need to be recorded into your Ledger.
This section covers the first of two steps to record payments in your Ledger:
There are two types of Links:
For each account in your external financial system, there's a corresponding External Account. External Accounts have a Tx for each transaction in that account.
After you set up a Link, create a Ledger Account in your Ledger linked to each External Account.
To create a Native Link with Stripe:
Every Balance Transaction at Stripe has two amount fields:
To handle this, Stripe Links create two Txs for each Balance Transaction at Stripe: one for the gross amount and the other for the fee. This allows you to account for these amounts independently in your Ledger. The external IDs for these Txs are:
{{stripe_tx_id}}_gross for the gross amount Tx{{stripe_tx_id}}_fee for the fee amount TxFor example, a Stripe Balance Transaction with ID txn_123 will result in two Txs in FRAGMENT with external IDs txn_123_gross and txn_123_fee. You can reconcile both of these in a single reconcileTx call.
To enable Stripe Connect, create a Restricted Access Key (RAK) with the following permissions:
Read for both Permissions and Connect PermissionsRead for both Permissions and Connect PermissionsRead for both Permissions and Connect PermissionsReadWrite. This is used to setup webhooks to FRAGMENT.Once you have the RAK:
Details pageCustom Links allow you to build your own integration between FRAGMENT and any external financial system. Instead of syncing automatically like a Native Link, you sync External Accounts and Txs by calling the API.
Your sync process can periodically enumerate transactions and accounts in your external system or be triggered by webhook.
You can either create a Link in the dashboard or using createCustomLink:
mutation NewCustomLink($name: String!, $ik: SafeString!) {
createCustomLink(name: $name, ik: $ik) {
__typename
... on CreateCustomLinkResult {
link {
id
name
}
isIkReplay
}
... on Error {
code
message
}
}
}{
"name": "JPM Chase",
"ik": "dev-chase"
}If you only have a few accounts, sync them manually as part of bootstrapping your Ledger.
Otherwise, sync accounts as you create them at the external system. Trigger the sync process either periodically, by webhook, or both.
Once you have a set of accounts to sync, call syncCustomAccounts:
mutation CreateBankAccounts(
$link: LinkMatchInput!
$accounts: [CustomAccountInput!]!
) {
syncCustomAccounts(link: $link, accounts: $accounts) {
__typename
... on SyncCustomAccountsResult {
accounts {
id
externalId
name
}
}
... on Error {
code
message
}
}
}{
"link": {
"id": "some-link-id"
},
"accounts": [
{
"externalId": "bank-account-1",
"name": "Operational Account"
},
{
"externalId": "bank-account-2",
"name": "Reserve Account"
}
]
}You should ensure that externalId is a stable and unique identifier for each account, within the scope of its Link. This ensures that syncing is idempotent. externalId is typically set to the ID of the account at the external system.
Calling syncCustomAccounts with a different name for an existing externalId updates the name of the External Account.
Settled payments at external financial systems will create transactions that need to be synced to FRAGMENT.
You may also want to sync and reconcile when your product makes a payment. You should only sync transactions that are settled, not pending or declined.
Once you have a set of transactions to sync, call syncCustomTxs:
mutation SyncTransactions(
$link: LinkMatchInput!
$txs: [CustomTxInput!]!
) {
syncCustomTxs(link: $link, txs: $txs) {
__typename
... on SyncCustomTxsResult {
txs {
id
externalId
amount
date
description
}
}
... on Error {
code
message
}
}
}{
"link": { "id": "some-link-id" },
"txs": [
{
"account": {
"externalId": "bank-account-1"
},
"externalId": "tx-1",
"description": "Processed ACH batch",
"amount": "-100",
"posted": "1968-01-01"
},
{
"account": {
"externalId": "bank-account-2"
},
"externalId": "tx-2",
"description": "Received RTP payment",
"amount": "100",
"posted": "1968-01-01T16:45:00Z"
}
]
}You should ensure that externalId is a stable and unique identifier for each transaction, within the scope of its account. This identifier enforces idempotentency.
This identifier is typically the ID of the transaction at the external system. Use the lowest level transaction ID available, not the ID of a higher level construct, like a payment that may be linked to multiple transactions. You can sync transactions from different accounts in the same API call, but they must all belong to the same Custom Link.
Calling syncCustomTxs with a different description for an existing externalId updates the description on the existing Tx. amount and posted timestamp are immutable and cannot be updated. Instead, you can delete the existing Tx and resync it with the updated fields.
To delete Txs on a Custom Link, call deleteCustomTxs with the Fragment IDs of the Txs you want to delete:
mutation DeleteCustomTxs($txs: [ID!]!) {
deleteCustomTxs(txs: $txs) {
__typename
... on DeleteCustomTxsResult {
txs {
tx {
id
externalId
posted
amount
sequence
isDeleted
deletedAt
}
}
}
... on Error {
code
message
}
}
}You must pass the FRAGMENT ID, not the transaction's External ID used at the external system. The FRAGMENT ID, serves as the idempotency key for this operation. Subsequent calls to deleteCustomTxs with the same FRAGMENT ID, will return a success response.
Once a Tx is deleted, you can call syncCustomTxs with the same externalId and different values. By deleting and resyncing, you can update immutable fields like amount and posted. The newly synced Tx will have a different FRAGMENT ID, to the original Tx. Calling deleteCustomTxs again with the original FRAGMENT ID, will return a success response, but will not delete the newly synced Tx.
Notes: