DDroomwork Developers

Version 1.0.0

Droomwork LEDGER

The double entry books behind everything that moves money.

Base URLhttps://sandbox.droomwork.io
AuthenticationDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…
Signed in: requests you run on this page use a key made for your account. Your keys →

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?

GET/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" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of LedgerAccount required

The records on this page, in the order the list promises. Empty when nothing matched.

9 fields of LedgerAccount
id string required

The 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" required

Always ledger_account. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False 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 required

True 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 required

The 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 required

What the account is for. There are four kinds and no more.

assetliabilityexpenseequity
currency string required

ISO 4217 code. An account holds one currency.

balance object required

Computed 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 required

A 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 required

ISO 4217 code.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
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)
Response
{
  "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
}
GET/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 optional

How 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 optional

The 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" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of LedgerPosting required

The records on this page, in the order the list promises. Empty when nothing matched.

9 fields of LedgerPosting
id string required

The 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" required

Always ledger_posting. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False 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 required

True 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 required

Derived 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 required

The 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 optional

The 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 required

The 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 required

The 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" required

Always ledger_posting_line. Tells you which kind of record you are looking at, so one handler can read any response.

account_id string required

The 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 required

The account's code, so a line reads without a second call.

direction string required

Which 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.

debitcredit
amount Money required
2 fields of Money
amount integer · int64 required

A 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 required

ISO 4217 code.

run_id string · nullable optional

The 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 optional

The 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 optional

The 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 required

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# 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)
Response
{
  "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
}
GET/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 required

The 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 required

The 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" required

Always ledger_posting. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False 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 required

True 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 required

Derived 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 required

The 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 optional

The 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 required

The 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 required

The 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" required

Always ledger_posting_line. Tells you which kind of record you are looking at, so one handler can read any response.

account_id string required

The 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 required

The account's code, so a line reads without a second call.

direction string required

Which 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.

debitcredit
amount Money required
2 fields of Money
amount integer · int64 required

A 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 required

ISO 4217 code.

run_id string · nullable optional

The 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 optional

The 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 optional

The 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 required

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
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)
Response
{
  "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"
}