Skip to content

Instantly share code, notes, and snippets.

@mrts
Last active December 7, 2025 19:39
Show Gist options
  • Select an option

  • Save mrts/abaf4b3ebc235fdac93a22fc561265fc to your computer and use it in GitHub Desktop.

Select an option

Save mrts/abaf4b3ebc235fdac93a22fc561265fc to your computer and use it in GitHub Desktop.
Merit Aktiva API quick example rewritten for Google Script
function testGetAllCustomers() {
const ApiId = '...';
const ApiKey = '...';
function pad2(n) {
return n > 9 ? '' + n : '0' + n;
}
function getTimestamp() {
var d = new Date();
var yyyy = d.getFullYear();
var MM = pad2(d.getMonth() + 1);
var dd = pad2(d.getDate());
var HH = pad2(d.getHours());
var mm = pad2(d.getMinutes());
var ss = pad2(d.getSeconds());
return '' + yyyy + MM + dd + HH + mm + ss; // YYYYMMDDHHmmss
}
// Empty filter, ask for all customers.
// Merit docs warn this can be very large and may fail for big accounts.
var queryPayload = {};
var timestamp = getTimestamp();
var dataString = ApiId + timestamp + JSON.stringify(queryPayload);
var hashBytes = Utilities.computeHmacSha256Signature(
dataString,
ApiKey,
Utilities.Charset.UTF_8
);
var signature = Utilities.base64Encode(hashBytes);
Logger.log('dataString: %s', dataString);
Logger.log('signature (Base64 HMAC-SHA256): %s', signature);
var baseUrl = 'https://aktiva.merit.ee/api/v1/getcustomers';
var url =
baseUrl +
'?ApiId=' + encodeURIComponent(ApiId) +
'&timestamp=' + encodeURIComponent(timestamp) +
'&signature=' + encodeURIComponent(signature);
var options = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(queryPayload),
muteHttpExceptions: true, // let us read non-200 responses
};
var response = UrlFetchApp.fetch(url, options);
Logger.log('Status code: %s', response.getResponseCode());
Logger.log('Headers: %s', JSON.stringify(response.getAllHeaders()));
var contentType = response.getHeaders()['Content-Type'] || '';
var bodyText = response.getContentText();
if (contentType.indexOf('application/json') !== -1) {
try {
var data = JSON.parse(bodyText);
if (Array.isArray(data)) {
Logger.log('Received %s customers', data.length);
Logger.log('Customers: %s', data);
} else {
Logger.log('JSON response (not an array): %s', JSON.stringify(data, null, 2));
}
} catch (e) {
Logger.log('Body (text, JSON parse failed): %s', bodyText);
}
} else {
Logger.log('Body (text): %s', bodyText);
}
}
function testMeritAktivaSendInvoice() {
const ApiId = '...';
const ApiKey = '...';
const KM24_ID = '...';
const DOCDATE = '20250113131239';
var reqJson = {
Customer: {
// Id: null,
Name: 'FirstCustomer Inc',
RegNo: '1122334455',
NotTDCustomer: false,
VatRegNo: '11223344',
CurrencyCode: 'EUR',
PaymentDeadLine: 7,
OverDueCharge: 0,
RefNoBase: 1,
Address: 'Merimiehenkatu 31',
CountryCode: 'FI',
County: 'Finland',
City: 'Helsinki',
PostalCode: '',
PhoneNo: '6548765',
PhoneNo2: '',
HomePage: '',
Email: '[email protected]',
},
DocDate: DOCDATE,
DueDate: DOCDATE,
InvoiceNo: '123',
RefNo: '1232',
DepartmentCode: '',
ProjectCode: '',
InvoiceRow: [
{
Item: {
Code: 1234567,
Description: 'Bag of goldflakes',
Type: 3,
UOMName: 'kg',
},
Quantity: '2',
Price: '1000',
DiscountPct: 0,
DiscountAmount: 0,
TaxId: KM24_ID,
LocationCode: 1,
},
],
TotalAmount: 2000,
RoundingAmount: 0,
TaxAmount: [{ TaxId: KM24_ID, Amount: 400 }],
HComment: '',
FComment: '',
};
function pad2(n) {
return n > 9 ? '' + n : '0' + n;
}
function getTimestamp() {
var d = new Date();
var yyyy = d.getFullYear();
var MM = pad2(d.getMonth() + 1);
var dd = pad2(d.getDate());
var HH = pad2(d.getHours());
var mm = pad2(d.getMinutes());
var ss = pad2(d.getSeconds());
return '' + yyyy + MM + dd + HH + mm + ss;
}
var timestamp = getTimestamp();
var dataString = ApiId + timestamp + JSON.stringify(reqJson);
var hashBytes = Utilities.computeHmacSha256Signature(
dataString,
ApiKey,
Utilities.Charset.UTF_8
);
var signature = Utilities.base64Encode(hashBytes);
Logger.log('dataString: %s', dataString);
Logger.log('signature (Base64 HMAC-SHA256): %s', signature);
var baseUrl = 'https://aktiva.merit.ee/api/v1/sendinvoice';
var url =
baseUrl +
'?ApiId=' + encodeURIComponent(ApiId) +
'&timestamp=' + encodeURIComponent(timestamp) +
'&signature=' + encodeURIComponent(signature);
var options = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(reqJson),
muteHttpExceptions: true, // let us read non-200 responses
};
var response = UrlFetchApp.fetch(url, options);
Logger.log('Status code: %s', response.getResponseCode());
Logger.log('Headers: %s', JSON.stringify(response.getAllHeaders()));
var contentType = response.getHeaders()['Content-Type'] || '';
var body = response.getContentText();
if (contentType.indexOf('application/json') !== -1) {
try {
var jsonBody = JSON.parse(body);
Logger.log('Body (JSON): %s', JSON.stringify(jsonBody, null, 2));
} catch (e) {
Logger.log('Body (text, JSON parse failed): %s', body);
}
} else {
Logger.log('Body (text): %s', body);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment