Akahu is New Zealand’s open finance platform.
Akahu builds and maintains data integrations with banks and other financial institutions. We bundle those integrations into a simple API for developers.
This SDK provides utilities for both node.js and client applications to simplify and enhance the usage of Akahu APIs.
Before you can get started using Akahu APIs, you will first need to register your application with Akahu. If you do not yet have an application registered with Akahu, use the Contact Us form to get in touch.
Once you have registered your Akahu application, you will be issued with the following:
- Your Akahu App ID Token
- Your Akahu App Secret
You will need to use these credentials when interacting with Akahu API endpoints.
Important: It is extremely important that you keep your App Secret secure. This means that it must not be used in client applications, which may expose the secret in their source code. Akahu API endpoints that require the use of your App Secret for authentication must only be accessed from server applications.
Using npm
:
npm install akahu
Using yarn
:
yarn add akahu
As an ES Module:
import { AkahuClient } from 'akahu';
As a CommonJS module:
const { AkahuClient } = require('akahu');
Fetching user and account data:
// Replace appToken with your App Token
const appToken = 'app_token_...';
// Replace with an OAuth user access token. See note below for details.
const userToken = 'user_token_...';
// Create an instance of the AkahuClient and fetch some information
const akahu = new AkahuClient({ appToken });
const user = await akahu.users.get(userToken);
const accounts = await akahu.accounts.list(userToken);
// Let's have a look at what we got back
console.log(`${user.email} has linked ${accounts.length} accounts:`);
for (const account of accounts) {
const { connection, name, formatted_account, balance } = account;
console.log(` ${connection.name} account "${name}" (${formatted_account}) ` +
`with available balance $${balance.available}.`);
}
// Example output:
// user@example.com has linked 2 accounts:
// Westpac account "Westpac Choice" (01-0137-0000000-00) with available balance $447.75.
// Westpac account "Westpac eSaver" (01-0137-0000000-01) with available balance $17019.34.
Note: If you are trialling Akahu using a Personal App, you will be able to find your App Token and User Token at https://my.akahu.nz/developers. Otherwise, the user token must first be obtained by completing the OAuth2 authorization flow.
- AkahuClient
- Errors
Akahu uses the OAuth2 authorization flow to allow your application to request authorization from users to access Akahu APIs on their behalf. This authorization flow consists of the following steps:
- Your application directs the user to the Akahu authorization page.
- The user logs in to Akahu and chooses which accounts they wish to authorize your application to access.
- The user is redirected back to your application along with a short-lived authorization code included in the URL query parameters.
- Your application server exchanges this authorization code with Akahu for a longer-lived user access token, which you can then use to authorize API requests on their behalf.
This SDK provides utilities to simplify integration of this authorization flow into your application.
The auth.buildAuthorizationUrl
helper simplifies generating
the link to direct the user to the Akahu authorization page. This helper can be run on either client or server.
The below example demonstrates a simple React component that will link the user to the Akahu authorization page when clicked.
import React from 'react';
import { AkahuClient } from 'akahu';
const akahu = new AkahuClient({
// Configure your app token here.
// App secret is not required and should not be included client-side.
appToken: process.env.AKAHU_APP_TOKEN,
});
// Configure your redirect uri (for step 3) here
const akahuOAuthRedirectUri = 'https://my.app.domain/auth/akahu';
export default LoginWithAkahuLink = () => {
const authUrl = akahu.auth.buildAuthorizationUrl({
redirect_uri: akahuOAuthRedirectUri,
email: '...', // Optionally prefill the users email address
});
return <a href={authUrl}>Login with Akahu</a>;
};
The authorization code exchange can be performed using the
auth.exchange
helper.
The below example shows a basic Express.js endpoint to handle the OAuth redirect (step 3) and complete the auth code exchange (step 4) to retrieve a user access token.
import express from 'express';
import { AkahuClient } from 'akahu';
const akahu = new AkahuClient({
// Configure your app token here and secret here.
// Both app token and secret are required to complete the auth code exchange
appToken: process.env.AKAHU_APP_TOKEN,
appSecret: process.env.AKAHU_APP_SECRET
});
const app = express();
// The redirect URI that was included as a parameter in the authorization request
// must also be included in the auth code exchange request to validate its authenticity.
const akahuOAuthRedirectUri = 'https://my.app.domain/auth/akahu';
app.get('/auth/akahu', async (req: express.Request, res: express.Response): void => {
// Exchange the auth code - this is included as a query parameter in the request
const tokenResponse = await akahu.auth.exchange(req.query.code, akahuOAuthRedirectUri);
const { access_token } = tokenResponse;
/*
...
Save access_token against your application user in the database.
...
*/
// Success! You can now use access_token to authorize Akahu API requests on behalf of the user.
res.sendStatus(200);
});
🧹 Best Practice
To ensure that your application does not retain unnecessary access to user data, revoke access tokens in the event that they are no longer required (e.g. the user deletes their account).
The transactions.list
method can be used to retrieve transactions
from accounts that the user has authorized your application to access.
Transaction responses are paginated (Akahu only returns small batches at a time), so we must page through them to get all of them.
import { AkahuClient } from "akahu";
// Optional type defs for Typescript
import type { Transaction, TransactionQueryParams } from "akahu";
const akahu = new AkahuClient({
appToken: process.env.AKAHU_APP_TOKEN,
});
// Replace with an OAuth user access token
const userToken = "user_token_...";
// Specify a start and end timestamp to filter by a date range. If no date range
// is provided, transactions from the last 30 days will be returned.
const query: TransactionQueryParams = {
// start: "2021-01-01T00:00:00.000Z",
// end: "2021-01-02T00:00:00.000Z",
};
const transactions: Transaction[] = [];
do {
// Transactions are returned one page at a time
const page = await akahu.transactions.list(userToken, query);
// Store the returned transaction `items` from each page
transactions.push(...page.items);
// Update the cursor to point to the next page
query.cursor = page.cursor.next;
// Continue until the server returns a null cursor
} while (query.cursor !== null);
console.log(`Retrieved ${transactions.length} transactions:`);
for (const transaction of transactions) {
console.log(transaction.description);
}
The transfers.create
method can be used to initiate
a bank transfer between two of a users connected bank accounts:
// Make a $5 transfer between these two accounts
const transfer = await akahu.transfers.create(
userToken,
{
from: "acc_1111111111111111111111111",
to: "acc_2222222222222222222222222",
amount: 5
}
);
console.log("Transfer Initiated:", transfer._id);
This example demonstrates a basic Express.js endpoint to receive and validate Akahu webhook events.
This endpoint follows the recommended webhook verification process as documented at https://developers.akahu.nz/docs/reference-webhooks#verifying-a-webhook.
By default, AkahuClient
uses an internal in-memory cache to avoid downloading
the webhook signing key each time a webhook is received. See
caching webhook signing keys for more advanced
caching options.
For a complete reference of the different webhook payloads that your application may receive, see https://developers.akahu.nz/docs/reference-webhooks#what-a-webhook-looks-like.
import express from "express";
import { AkahuClient } from 'akahu';
// Optional type defs for Typescript
import type { WebhookPayload } from 'akahu';
// IMPORTANT: initialize the client globally to make use of built in public key
// caching. Initializing a new client per-request would cause the public key to
// be downloaded from Akahu servers for every webhook that is received.
const akahu = new AkahuClient({
appToken: process.env.AKAHU_APP_TOKEN,
appSecret: process.env.AKAHU_APP_SECRET
});
// Initialize the express app
const app = express();
// Use `express.raw({type: 'application/json'})` to get the raw request body.
// The raw, unparsed body is required to validate the webhook signature.
app.post('/akahu-webhook', express.raw({type: 'application/json'}), async (req, res) => {
// This signature will be used to validate the authenticity of the webhook payload.
const signature = req.headers['X-Akahu-Signature'];
// This is the ID of the signing key that was used to generate the signature
const keyId = req.headers['X-Akahu-Signing-Key']
let payload: WebhookPayload;
// The AkahuClient will lookup the public key that matches `keyId` and use this
// key to validate the webhook signature.
try {
// If validation is successful, the JSON payload is deserialized and returned.
payload = await akahu.webhooks.validateWebhook(keyId, signature, req.body);
} catch (e) {
console.log(`Webhook validation failed: ${e.message}`);
return res.status(400).send(e.message);
}
// Do something with the webhook payload.
const { webhook_type, webhook_code, ...params } = payload;
console.log(`Received webhook type: '${webhook_type}', code: ${webhook_code}:`);
console.log(params);
// Return a 200 response to acknowledge receipt of the webhook
res.sendStatus(200);
});
The previous example makes use of the in-memory caching of the webhook signing key by AkahuClient
to avoid making excessive requests to the Akahu API. However, this caching may not be effective if
your application is deployed as a stateless/ephemeral function (e.g. using AWS Lambda). In such
cases, it is recommended to use an external cache such as redis or memcached to allow shared
caching between invocations of your application.
To make use of an external cache to store the webhook signing key, supply the optional cacheConfig
config object to validateWebhook()
.
The cache
attribute of this object must implement the WebhookSigningKeyCache
interface to provide access to the external cache. See WebhookCacheConfig
for the complete set of caching configuration that is available.
The below example wraps an instance of the node-redis
client get
and set
methods to provide this interface:
import { promisify } from "util";
import { createClient } from 'redis';
const redis = createClient(/* ... */);
const cacheConfig = {
cache: {
// Convert redis client methods to promise friendly implementations
get: promisify(redis.get).bind(redis),
set: promisify(redis.set).bind(redis),
}
};
/* ... */
try {
payload = await akahu.webhooks.validateWebhook(keyId, signature, req.body, cacheConfig);
} catch (e) {
/* ... */
}