Version 1.0.0
Heros 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, not 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.
Your books start empty, and stay empty until your chart of accounts exists. A posting's line can only name an account that is already there, and no endpoint here creates one. Until the chart is in place, GET /v1/ledger/accounts answers with an empty list and there is nothing yet to read at the other two.
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#See what each account holds
ledger.accounts.list
This answers what every account of yours stands at right now, so your product can show a trial balance of its own without adding anything up or holding a figure that falls behind.
Send nothing; this takes no parameters. Your whole chart comes back in code order, with every account in it, whether or not anything has reached one yet.
This is the one list here that does not page. There is no cursor to follow and no page size to raise, so what comes back is your whole chart and you can total it as it stands.
Returns
Every account you hold, each with the balance it stands at.
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 lac_; 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.
mocked
boolean
requiredTrue when this response came from a mock, false when the engine computed the numbers.
It stays true for a capability that is still on its documented mock, even in the live realm. It answers a different question from livemode: livemode says which realm you are in, this says where the numbers came from.
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 '@heros/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 heros_sdk
config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.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 = HerosSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\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.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.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 Heros.Sdk.Api;
using Heros.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 heros "github.com/fenibofubara/heros-sdk-go"
ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.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": "lac_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_account",
"livemode": false,
"mocked": false,
"code": "payroll_payable",
"kind": "asset",
"currency": "NGN",
"balance": {
"amount": 41250000,
"currency": "NGN"
},
"created_at": "2026-09-01T09:00:00Z"
}
],
"has_more": true
}
/v1/ledger/postings#Go through every movement, newest first
ledger.postings.list
This answers what has moved through your books, so your product can walk every movement your organisation holds and find the one behind a figure somebody is asking about.
Send nothing and they come back most recently recorded first. One posting is one movement, so a figure you are chasing sits inside one of them and never across several.
That order is when a posting was recorded, and occurred_on is the date the movement belongs to. The two part company whenever a period is posted after it closes, so page on the order and reconcile on occurred_on.
A starting_after that is not one of your postings answers with an empty page, the same answer you get on reaching the end, so send an id you read from here.
Open one in full at GET /v1/ledger/postings/{posting_id}.
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
One page of movements, newest first, each with the lines behind it.
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 lpo_; 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.
mocked
boolean
requiredTrue when this response came from a mock, false when the engine computed the numbers.
It stays true for a capability that is still on its documented mock, even in the live realm. It answers a different question from livemode: livemode says which realm you are in, this says where the numbers came from.
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 lpo_; 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 lpo_; 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 lac_. 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 '@heros/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 heros_sdk
config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.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 = HerosSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\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.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.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 Heros.Sdk.Api;
using Heros.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 heros "github.com/fenibofubara/heros-sdk-go"
ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.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": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting",
"livemode": false,
"mocked": false,
"natural_key": "payroll_september",
"occurred_on": "2026-09-01",
"lines": [
{
"id": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "lac_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "example",
"direction": "debit",
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
{
"id": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "lac_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "example",
"direction": "debit",
"amount": {
"amount": 7500000,
"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}#Check one movement, line by line
ledger.postings.retrieve
This answers with one movement in full, so when somebody queries a single figure you can open the posting behind it and show every account that figure moved.
Send the posting's id in the path, the one you read from GET /v1/ledger/postings or from another posting's reverses_id, which only a correction carries, naming the posting it reverses.
A posting that is not yours answers not_found, which is the same answer as one that never existed.
Path parameters
posting_id
string
requiredThe posting's id, starting with lpo_, from a posting you listed at GET /v1/ledger/postings. One that isn't yours answers not_found, not forbidden.
Returns
The movement you asked for, with every line it is made of.
id
string
requiredThe posting's identifier, starting with lpo_; 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.
mocked
boolean
requiredTrue when this response came from a mock, false when the engine computed the numbers.
It stays true for a capability that is still on its documented mock, even in the live realm. It answers a different question from livemode: livemode says which realm you are in, this says where the numbers came from.
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 lpo_; 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 lpo_; 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 lac_. 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/lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, LEDGERApi } from '@heros/sdk';
const api = new LEDGERApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.ledgerPostingsRetrieve({ postingId: 'lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/ledger/postings/lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
method: 'GET',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
},
});
const result = await response.json();import os
import heros_sdk
config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.LEDGERApi(client)
result = api.ledger_postings_retrieve(posting_id='lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/ledger/postings/lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
},
)
result = response.json()<?php
require_once __DIR__ . '/vendor/autoload.php';
$config = HerosSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\LEDGERApi(new GuzzleHttp\Client(), $config);
$result = $api->ledgerPostingsRetrieve(posting_id: 'lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/ledger/postings/lpo_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.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.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("lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/ledger/postings/lpo_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 Heros.Sdk.Api;
using Heros.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: "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/ledger/postings/lpo_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 heros "github.com/fenibofubara/heros-sdk-go"
ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)
result, _, err := client.LEDGERAPI.LedgerPostingsRetrieve(ctx, "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/ledger/postings/lpo_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": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting",
"livemode": false,
"mocked": false,
"natural_key": "payroll_september",
"occurred_on": "2026-09-01",
"lines": [
{
"id": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "lac_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "example",
"direction": "debit",
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"run_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"payslip_line_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
{
"id": "lpo_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "ledger_posting_line",
"account_id": "lac_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"account_code": "example",
"direction": "debit",
"amount": {
"amount": 7500000,
"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"
}