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 PermissionsRead
Write
. 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: