Server-to-server clients for open-banking.io in .NET, Node, Python, Rust, Go, Java, Ruby, and PHP.
open-banking.io is zero-knowledge: the service stores and returns only ciphertext it cannot read. These SDKs do the two things an integrator needs — authenticate with your API key and decrypt the data locally with your exported private key — and hand you clean, typed models.
| Language | Package | Path |
|---|---|---|
| .NET | OpenBankingIO.Client (NuGet) |
dotnet/ |
| Node / TypeScript | @open-banking-io/client (npm) |
node/ |
| Python | open-banking-io (PyPI) |
python/ |
| Rust | open-banking-io (crates.io) |
rust/ |
| Go | github.com/open-banking-io/clients/go |
go/ |
| Java | io.open-banking:open-banking-io-client (Maven Central) |
java/ |
| Ruby | open-banking-io (RubyGems) |
ruby/ |
| PHP | open-banking-io/client (Packagist) |
php/ |
- In the app, export your credentials bundle (
.json) — it contains yourapiBaseUrl, an API key, and your encryption private key (PKCS#8). - Point an SDK at the bundle. Every request sends
X-Api-Key; every response is decrypted in-process.
// .NET
using var client = OpenBankingClient.FromCredentials("credentials.json");
foreach (var a in await client.GetAccountsAsync())
Console.WriteLine($"{a.Iban}: {a.Balances.First(b => b.Type == "ITBD").Amount} {a.Currency}");// Node
const client = OpenBankingClient.fromCredentials("credentials.json");
for (const a of await client.getAccounts())
console.log(a.iban, a.balances.find(b => b.type === "ITBD")?.amount, a.currency);# Python
client = OpenBankingClient.from_credentials("credentials.json")
for a in client.get_accounts():
booked = next(b for b in a.balances if b.type == "ITBD")
print(a.iban, booked.amount, a.currency)// Rust
let client = OpenBankingClient::from_credentials("credentials.json")?;
for a in client.get_accounts()? {
let booked = a.balances.iter().find(|b| b.type_ == "ITBD");
println!("{:?} {:?} {}", a.iban, booked.map(|b| &b.amount), a.currency);
}// Go
client, _ := openbanking.FromCredentials("credentials.json", nil)
accounts, _ := client.GetAccounts()
for _, a := range accounts {
for _, b := range a.Balances {
if b.Type == "ITBD" { fmt.Println(a.Iban, b.Amount, a.Currency) }
}
}// Java
var client = OpenBankingClient.fromCredentials("credentials.json");
for (Account a : client.getAccounts())
a.balances().stream().filter(b -> b.type().equals("ITBD")).findFirst()
.ifPresent(b -> System.out.println(a.iban() + " " + b.amount() + " " + a.currency()));# Ruby
client = OpenBankingIO::Client.from_credentials("credentials.json")
client.get_accounts.each do |a|
booked = a.balances.find { |b| b.type == "ITBD" }
puts "#{a.iban} #{booked&.amount} #{a.currency}"
end// PHP
$client = OpenBankingIO\Client::fromCredentials("credentials.json");
foreach ($client->getAccounts() as $a) {
foreach ($a->balances as $b) {
if ($b->type === "ITBD") echo "{$a->iban} {$b->amount} {$a->currency}\n";
}
}All eight expose the same surface: getAccounts, getTransactions(accountId, …), getConnections,
sync(accountId), syncAll(). Sync decrypts the account's session uid locally and posts it, so the
service can refresh from the bank without ever holding it in plaintext.
Each sensitive value is an envelope: version(1) | ephemeralPublicKey(65) | nonce(12) | tag(16) | ciphertext, produced with ephemeral ECDH on P-256 → HKDF-SHA256 (salt = 32 zero bytes, info =
bank.core.ci/zk/v1) → AES-256-GCM. Only your private key can open it. All eight SDKs are verified
against the same fixtures (fixtures/) so they decrypt identically and interoperate with the live
service's wire format.
# regenerate the shared test fixtures (keypair + encrypted sample responses)
node tools/generate-fixtures.mjs
# run each SDK's tests (crypto round-trip + a mock-server integration suite)
dotnet test dotnet/
cd node && npm install && npm test
cd python && pip install -e .[dev] && pytest -q
cd rust && cargo test
cd go && go test ./...
cd java && mvn -B verify
cd ruby && bundle install && bundle exec rspec
cd php && composer install && vendor/bin/phpunitCI (.github/workflows/ci.yml) builds and tests all eight on every push. Releases are per-package:
each package publishes from its own <dir>/vX.Y.Z tag (e.g. node/v0.2.0), so tagging one package never
republishes the others. Cut a release from Actions → Release (pick a package + version) — see
RELEASING.md. Targets: NuGet, npm, PyPI, crates.io, Maven Central, RubyGems, Packagist,
and the Go module proxy.
Zero-knowledge security model and trust boundaries: THREAT_MODEL.md. Report
vulnerabilities privately per SECURITY.md. Contributions welcome — see
CONTRIBUTING.md, CODE_OF_CONDUCT.md, and
SUPPORT.md.
MIT licensed.