Version 1.0.0
Droomwork LEDGER
The double entry books behind everything that moves money.
https://sandbox.droomwork.ioDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…Every movement of your money is recorded as a posting: two or more lines that sum to zero. This is where you read them.
What you should know before you start
You can only read here, and that won't change. There's no endpoint that writes a posting and no ledger:write scope to hold. A posting exists because a module caused it.
A posting is its lines. The lines sum to zero, and they come back with the posting rather than behind a second call. Debits and credits are both positive numbers; the direction carries the sign.
Balances are computed, never stored. An account's balance is worked out from its lines at the moment you ask.
A correction is another posting. Nothing here is ever edited or deleted. A posting that reverses another names it in reverses_id.
Money is whole minor units. An amount is an integer count of the currency's smallest unit plus the currency code. For NGN that's kobo. Read how many minor units make one major unit from GET /v1/currencies; don't assume it's a hundred.
Getting started
You need a credential carrying ledger:read. Sandbox keys from the developer portal carry it. Read the chart of accounts first: every posting line names an account by its code, and balances are grouped by code.
Every operation below has two samples: Direct HTTP, with nothing but your language's own client, and Client library, with ours. Same request, same answer; the switch above each sample picks one and remembers it. Which should I use?
/v1/ledger/accounts#List accounts and their balances
ledger.accounts.list
Your chart of accounts, in code order, each account with what it currently holds.
The balance is computed from the account's lines at the moment you ask. An account that has never been posted to returns a balance of zero, not no balance: the account exists, and absence and emptiness are different answers.
Debits are positive and credits negative, so the balances of the accounts touched by one posting sum to zero.
Returns
The chart of accounts.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of LedgerAccount
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
9 fields of LedgerAccount
id
string
requiredThe account's identifier, starting with ledger_account_; it never changes. Each posting line names this account by it in account_id at GET /v1/ledger/postings.
object
always "ledger_account"
requiredAlways ledger_account. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.
mocked
boolean
requiredTrue when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10.
It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.
code
string
requiredThe name a posting line refers to the account by, unique within your organisation. Lines name a code rather than an identifier, so you can read a chart without a lookup.
kind
string
requiredWhat the account is for. There are four kinds and no more.
assetliabilityexpenseequitycurrency
string
requiredISO 4217 code. An account holds one currency.
balance
object
requiredComputed from the account's lines when you ask, never stored. Debits positive, credits negative. Zero for an account with no lines yet.
2 fields
amount
integer · int64
requiredA whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.
currency
string
requiredISO 4217 code.
created_at
string · date-time
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/ledger/accounts" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, LEDGERApi } from '@droomwork/sdk';
const api = new LEDGERApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.ledgerAccountsList({});const response = await fetch('https://sandbox.droomwork.io/v1/ledger/accounts', {
method: 'GET',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
},
});
const result = await response.json();import os
import droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.LEDGERApi(client)
result = api.ledger_accounts_list()import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/ledger/accounts',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
},
)
result = response.json()<?php
require_once __DIR__ . '/vendor/autoload.php';
$config = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\LEDGERApi(new GuzzleHttp\Client(), $config);
$result = $api->ledgerAccountsList();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/ledger/accounts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
],
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.LedgerApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
LedgerApi api = new LedgerApi(client);
var result = api.ledgerAccountsList();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/ledger/accounts"))
.header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;
var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new LEDGERApi(config);
var result = api.LedgerAccountsList();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/ledger/accounts");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();import droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
result, _, err := client.LEDGERAPI.LedgerAccountsList(ctx).Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/ledger/accounts", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
{
"object": "list",
"data": [
{
"id": "ledger_account_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_account",
"livemode": true,
"mocked": false,
"code": "payroll_payable",
"kind": "asset",
"currency": "NGN",
"balance": {
"amount": 1234567,
"currency": "NGN"
},
"created_at": "2026-09-01T09:00:00Z"
}
],
"has_more": true
}
/v1/ledger/postings#List postings
ledger.postings.list
Your postings, newest first, each with its lines.
Page as you do everywhere else on the platform: pass the identifier of the last posting you saw as starting_after. An identifier that isn't yours returns an empty page rather than a refusal; a refusal would confirm that it exists.
Query parameters
limit
integer
optionalHow many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.
starting_after
string
optionalThe id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.
Returns
A page of postings.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of LedgerPosting
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
9 fields of LedgerPosting
id
string
requiredThe posting's identifier, starting with ledger_posting_; it never changes. Pass it as posting_id to GET /v1/ledger/postings/{posting_id}, or as starting_after when paging GET /v1/ledger/postings.
object
always "ledger_posting"
requiredAlways ledger_posting. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.
mocked
boolean
requiredTrue when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10.
It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.
natural_key
string
requiredDerived from the source document, so re-posting the same source produces no second posting. A retry after a timeout is safe because of this.
occurred_on
string · date
requiredThe date the movement belongs to, which is not always the date it was recorded. A September payroll posted in October occurred in September.
reverses_id
string · nullable
optionalThe id of the posting this one reverses, starting with ledger_posting_; null unless this posting is a correction. Retrieve that posting at GET /v1/ledger/postings/{posting_id}.
lines
array of LedgerPostingLine
requiredThe lines that make up the posting, at least two, each naming an account, a direction and a positive amount. They sum to zero and come back with the posting rather than behind a second call.
9 fields of LedgerPostingLine
id
string
requiredThe line's identifier, starting with ledger_posting_; it never changes. Quote it when you ask about one line of a posting you read at GET /v1/ledger/postings or GET /v1/ledger/postings/{posting_id}.
object
always "ledger_posting_line"
requiredAlways ledger_posting_line. Tells you which kind of record you are looking at, so one handler can read any response.
account_id
string
requiredThe id of the account this line moves, starting with ledger_account_. It matches one account in your chart at GET /v1/ledger/accounts; account_code carries that account's code.
account_code
string
requiredThe account's code, so a line reads without a second call.
direction
string
requiredWhich side of the posting this line is on: debit counts as positive, credit as negative. amount is positive either way, and debits less credits across a posting's lines come to zero.
debitcreditamount
Money
required2 fields of Money
amount
integer · int64
requiredA whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.
currency
string
requiredISO 4217 code.
run_id
string · nullable
optionalThe id of the payroll run this line came from, starting with run_enterprise_, as POST /v1/payroll/runs and GET /v1/payroll/runs return it; null when no run produced the movement. Retrieve it at GET /v1/payroll/runs/{run_id}.
payslip_id
string · nullable
optionalThe id of the payslip this line came from, starting with run_enterprise_payslip_, as listed at GET /v1/payroll/payslips; null for a movement no payslip produced. Retrieve it at GET /v1/payroll/payslips/{payslip_id}.
payslip_line_id
string · nullable
optionalThe id of the payslip line this amount traces to: an entry in lines on the payslip at GET /v1/payroll/payslips/{payslip_id}, found by payslip_id; null when no payslip line produced it.
created_at
string · date-time
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# query parameters: limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/ledger/postings?limit=25" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, LEDGERApi } from '@droomwork/sdk';
const api = new LEDGERApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: limit (optional), starting_after (optional)
const result = await api.ledgerPostingsList({ limit: 25 });// query parameters: limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/ledger/postings?limit=25', {
method: 'GET',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
},
});
const result = await response.json();import os
import droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.LEDGERApi(client)
# query parameters: limit (optional), starting_after (optional)
result = api.ledger_postings_list(limit=25)import os
import requests
# query parameters: limit (optional), starting_after (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/ledger/postings?limit=25',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
},
)
result = response.json()<?php
require_once __DIR__ . '/vendor/autoload.php';
$config = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\LEDGERApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional), starting_after (optional)
$result = $api->ledgerPostingsList(limit: 25);<?php
// query parameters: limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/ledger/postings?limit=25');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
],
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.LedgerApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
LedgerApi api = new LedgerApi(client);
// query parameters: limit (optional), starting_after (optional)
var result = api.ledgerPostingsList(25, null);// query parameters: limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/ledger/postings?limit=25"))
.header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;
var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new LEDGERApi(config);
// query parameters: limit (optional), starting_after (optional)
var result = api.LedgerPostingsList(limit: 25);// query parameters: limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/ledger/postings?limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();import droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
// query parameters: limit (optional), starting_after (optional)
result, _, err := client.LEDGERAPI.LedgerPostingsList(ctx).Limit(25).Execute()// query parameters: limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/ledger/postings?limit=25", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
{
"object": "list",
"data": [
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting",
"livemode": true,
"mocked": false,
"natural_key": "payroll_september",
"occurred_on": "2026-09-01",
"lines": [
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "ledger_account_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "no_payee_destination",
"direction": "debit",
"amount": {
"amount": 1234567,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "ledger_account_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "no_payee_destination",
"direction": "debit",
"amount": {
"amount": 1234567,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
],
"created_at": "2026-09-01T09:00:00Z",
"reverses_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
],
"has_more": true
}
/v1/ledger/postings/{posting_id}#Retrieve a posting
ledger.postings.retrieve
One posting and its lines.
A posting that belongs to another organisation answers not_found, not forbidden; a refusal would confirm which of the two it was.
Path parameters
posting_id
string
requiredThe posting's id, starting with ledger_posting_, from a posting you listed at GET /v1/ledger/postings. One that isn't yours answers not_found, not forbidden.
Returns
The posting.
id
string
requiredThe posting's identifier, starting with ledger_posting_; it never changes. Pass it as posting_id to GET /v1/ledger/postings/{posting_id}, or as starting_after when paging GET /v1/ledger/postings.
object
always "ledger_posting"
requiredAlways ledger_posting. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.
mocked
boolean
requiredTrue when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10.
It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.
natural_key
string
requiredDerived from the source document, so re-posting the same source produces no second posting. A retry after a timeout is safe because of this.
occurred_on
string · date
requiredThe date the movement belongs to, which is not always the date it was recorded. A September payroll posted in October occurred in September.
reverses_id
string · nullable
optionalThe id of the posting this one reverses, starting with ledger_posting_; null unless this posting is a correction. Retrieve that posting at GET /v1/ledger/postings/{posting_id}.
lines
array of LedgerPostingLine
requiredThe lines that make up the posting, at least two, each naming an account, a direction and a positive amount. They sum to zero and come back with the posting rather than behind a second call.
9 fields of LedgerPostingLine
id
string
requiredThe line's identifier, starting with ledger_posting_; it never changes. Quote it when you ask about one line of a posting you read at GET /v1/ledger/postings or GET /v1/ledger/postings/{posting_id}.
object
always "ledger_posting_line"
requiredAlways ledger_posting_line. Tells you which kind of record you are looking at, so one handler can read any response.
account_id
string
requiredThe id of the account this line moves, starting with ledger_account_. It matches one account in your chart at GET /v1/ledger/accounts; account_code carries that account's code.
account_code
string
requiredThe account's code, so a line reads without a second call.
direction
string
requiredWhich side of the posting this line is on: debit counts as positive, credit as negative. amount is positive either way, and debits less credits across a posting's lines come to zero.
debitcreditamount
Money
required2 fields of Money
amount
integer · int64
requiredA whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.
currency
string
requiredISO 4217 code.
run_id
string · nullable
optionalThe id of the payroll run this line came from, starting with run_enterprise_, as POST /v1/payroll/runs and GET /v1/payroll/runs return it; null when no run produced the movement. Retrieve it at GET /v1/payroll/runs/{run_id}.
payslip_id
string · nullable
optionalThe id of the payslip this line came from, starting with run_enterprise_payslip_, as listed at GET /v1/payroll/payslips; null for a movement no payslip produced. Retrieve it at GET /v1/payroll/payslips/{payslip_id}.
payslip_line_id
string · nullable
optionalThe id of the payslip line this amount traces to: an entry in lines on the payslip at GET /v1/payroll/payslips/{payslip_id}, found by payslip_id; null when no payslip line produced it.
created_at
string · date-time
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, LEDGERApi } from '@droomwork/sdk';
const api = new LEDGERApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.ledgerPostingsRetrieve({ postingId: 'ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
method: 'GET',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
},
});
const result = await response.json();import os
import droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.LEDGERApi(client)
result = api.ledger_postings_retrieve(posting_id='ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
},
)
result = response.json()<?php
require_once __DIR__ . '/vendor/autoload.php';
$config = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\LEDGERApi(new GuzzleHttp\Client(), $config);
$result = $api->ledgerPostingsRetrieve(posting_id: 'ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
],
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.LedgerApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
LedgerApi api = new LedgerApi(client);
var result = api.ledgerPostingsRetrieve("ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
.header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;
var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new LEDGERApi(config);
var result = api.LedgerPostingsRetrieve(postingId: "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();import droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
result, _, err := client.LEDGERAPI.LedgerPostingsRetrieve(ctx, "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/ledger/postings/ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting",
"livemode": true,
"mocked": false,
"natural_key": "payroll_september",
"occurred_on": "2026-09-01",
"lines": [
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "ledger_account_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "no_payee_destination",
"direction": "debit",
"amount": {
"amount": 1234567,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
{
"id": "ledger_posting_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "ledger_account_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "no_payee_destination",
"direction": "debit",
"amount": {
"amount": 1234567,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
],
"created_at": "2026-09-01T09:00:00Z",
"reverses_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}