totalum-api-sdk
v3.0.7
Published
Totalum sdk wrapper and utils of totalum api
Readme
Totalum API SDK
Official TypeScript/JavaScript SDK for the Totalum API. This library provides a fully typed, modern interface to interact with all Totalum endpoints.
Installation
npm i totalum-api-sdkNote: v3.0 requires Node.js 18+ (for native fetch support). If you're using an older Node.js version, you'll need to polyfill fetch or upgrade to Node.js 18+.
Usage of TotalumApiSdk
Totalum allows you to use the SDK to perform actions such as creating records, managing files, creating PDFs, sending emails, and more.
The SDK is a fully-typed wrapper around the Totalum REST API.
What happens if you are not programming in javascript?
If you are not programming in javascript, you can use the api directly, see TOTALUM API DOCUMENTATION
Authentication
Note: If you use totalumSdk inside a totalum plugin, you don't need to authenticate, you can start using totalum like this: modules.totalumSdk.your.function();-> example: modules.totalumSdk.crud.query('your_table')
You can choose to use one of the two authentication methods offered by Totalum Sdk:
Token: You can use an access token to authenticate. This token can be obtained from the localStorage of your browser from the web https://web.totalum.app
ApiKey: (RECOMMENDED OPTION) You can use an ApiKey to authenticate. This ApiKey can be obtained in the Api Keys section of the Configuration section of Totalum.
ES Module Import:
import {AuthOptions, TotalumApiSdk} from 'totalum-api-sdk';
// CHOICE FROM USE accessToken OR apiKey (API KEY IS RECOMMENDED)
// the auth using token
const options: AuthOptions = {
token:{
accessToken: 'YOUR TOKEN' // get it from totalum project web localStorage
}
}
// the auth using api key
const options: AuthOptions = {
apiKey:{
'api-key': 'your_api_key', //the api key secret that is shown only once, example: sk_23r23r23r...
}
}
const totalumClient = new TotalumApiSdk(options);
// execute some TotalumApiSdk function
const result = await totalumClient.crud.query('your_table_name');
CommonJS Require:
const totalum = require('totalum-api-sdk');
// CHOICE FROM USE accessToken OR apiKey (API KEY IS RECOMMENDED)
// the auth using token
const options = {
token:{
accessToken: 'YOUR TOKEN' // get it from totalum project web localStorage
}
}
// the auth using api key
const options = {
apiKey:{
'api-key': 'your_api_key', //the api key secret that is shown only once, example: sk_23r23r23r...
}
}
const totalumClient = new totalum.TotalumApiSdk(options);
// execute some TotalumApiSdk function
const result = await totalumClient.crud.query('your_table_name');
HTML script import: (Use this way if you are using standalone html, and you cannot import npm packages)
<head>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/totalum-sdk.min.js"></script>
</head>
<script>
//Example of use TotalumSdk in your custom html page
const token=localStorage.getItem('token');
var totalumClient = new totalumSdk.TotalumApiSdk({
token:{
accessToken: token
}
});
const tableName = 'your-table-name'; // replace with your table name
//example of endpoint execution
totalumClient.crud.query(tableName).then((result) => {
if (result.errors) {
console.error("ERROR:", result.errors);
return;
}
const records = result.data;
console.log("THE RECORDS", records);
});
</script>Response Structure
All SDK methods return a TotalumApiResponse<T> object with the following structure:
interface TotalumApiResponse<T> {
data?: T; // The successful response data
errors?: ErrorResult; // Error information if request failed
}
interface ErrorResult {
errorCode: string;
errorMessage: string;
hasManyErrors?: boolean;
multipleErrors?: Array<{ errorCode: string; errorMessage: string }>;
}Example:
const result = await totalumClient.crud.getRecordById('users', 'user-id-123');
// Always check for errors first
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
console.error('Error Code:', result.errors.errorCode);
return;
}
// Now you can safely use result.data
const user = result.data;
console.log('User:', user);Error Handling
The SDK provides a TotalumError class for structured error handling:
import { TotalumError } from 'totalum-api-sdk';
const result = await totalumClient.crud.getRecordById('users', 'invalid-id');
if (result.errors) {
// You can create a TotalumError for better error handling
const error = new TotalumError(result.errors);
console.error('Error Code:', error.errorCode);
console.error('Error Message:', error.errorMessage);
// Check for multiple errors
if (error.hasManyErrors && error.multipleErrors) {
error.multipleErrors.forEach(err => {
console.error(`- ${err.errorCode}: ${err.errorMessage}`);
});
}
}Functions for create, read, filter, update, delete. (CRUD)
Get record by id
// Get a single record by id
const tableName = 'users'; // replace with your table name
const recordId = 'record-id-123'; // replace with your record id
const result = await totalumClient.crud.getRecordById(tableName, recordId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const record = result.data;
console.log('Record:', record);
Query records (Recommended)
totalumClient.crud.query() is the recommended method for reading data. It replaces getRecords, getNestedData, and getManyToManyReferencesRecords.
// Get records from a table (default: first 50 records)
const result = await totalumClient.crud.query('users');
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const records = result.data;
console.log('Records:', records);
Query with nested relations
// Get clients with all orders and the products of each order
const result = await totalumClient.crud.query('client', {
order: {
product: true
}
});
const clients = result.data;
Query with filters, sort, and pagination
// Get clients named 'John', sorted by name, limited to 10 results
const result = await totalumClient.crud.query('client', {
_filter: { name: 'John' },
_sort: { name: 'asc' },
_limit: 10
});
Query with OR conditions
const result = await totalumClient.crud.query('users', {
_filter: {
_or: [
{ role: 'admin' },
{ role: 'moderator' }
]
}
});
Query with nested filters and counts
// Get companies with active employees, count them, sort by name
const result = await totalumClient.crud.query('company', {
_filter: { country: 'Spain' },
_sort: { name: 'asc' },
_limit: 10,
employee: {
_filter: { status: 'active' },
_has: true,
_count: true,
task: { _sort: { createdAt: 'desc' }, _limit: 5 }
}
});
Query manyToMany relations
// Get students with their courses (manyToMany)
const result = await totalumClient.crud.query('students', {
courses: true
});
For full query documentation see: Read and Filter Data
Delete record by id
// Delete a record by id
const tableName = 'users';
const recordId = 'record-id-123';
const result = await totalumClient.crud.deleteRecordById(tableName, recordId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Deleted count:', result.data.deletedCount);
console.log('Acknowledged:', result.data.acknowledged);
Edit record by id
// Edit a record by id (you can edit one or multiple properties at the same time)
const tableName = 'users';
const recordId = 'record-id-123';
const updates = {
name: 'John Doe',
email: '[email protected]',
age: 30
};
const result = await totalumClient.crud.editRecordById(tableName, recordId, updates);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
// Returns the full updated record
const updatedRecord = result.data;
console.log('Updated record:', updatedRecord);
console.log('New name:', updatedRecord.name);
console.log('New email:', updatedRecord.email);
Create record
// Create a new record (you need to pass at least the required properties)
const tableName = 'users';
const newRecord = {
name: 'Jane Doe',
email: '[email protected]',
age: 25
};
const result = await totalumClient.crud.createRecord(tableName, newRecord);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
// Returns the full created record
const createdRecord = result.data;
console.log('Created record:', createdRecord);
console.log('Record ID:', createdRecord._id);
console.log('Name:', createdRecord.name);
Add many-to-many reference (add a reference to another record with a many-to-many relationship)
const tableName = 'students';
const recordId = 'student-id-123';
const propertyName = 'courses'; // the property that defines the many-to-many relationship
const referenceId = 'course-id-456'; // the id of the record to link
const result = await totalumClient.crud.addManyToManyReferenceRecord(tableName, recordId, propertyName, referenceId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Reference added successfully');
Drop many-to-many reference (remove a reference from a many-to-many relationship)
const tableName = 'students';
const recordId = 'student-id-123';
const propertyName = 'courses';
const referenceId = 'course-id-456'; // the id of the record to unlink
const result = await totalumClient.crud.dropManyToManyReferenceRecord(tableName, recordId, propertyName, referenceId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Reference removed successfully');
Get many-to-many references
Note: For reading manyToMany data, prefer using
totalumClient.crud.query()with the relation property expanded (see "Query manyToMany relations" above).
Functions for filter data
Note: All filtering examples below use
totalumClient.crud.query(), the recommended method. See "Query records" section above for more details.
Filter data using AND filter
// Get records applying AND filter (all conditions must be true)
const result = await totalumClient.crud.query('users', {
_filter: {
name: 'John',
email: { regex: '@example.com', options: 'i' },
age: { gte: 18, lte: 65 },
createdAt: { gte: new Date('2024-01-01'), lte: new Date('2024-12-31') }
},
_sort: { name: 'asc' },
_limit: 50
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const records = result.data;
console.log('Filtered records:', records);
Filter data using OR filter
// Get records applying OR filter (at least one condition must be true)
const result = await totalumClient.crud.query('users', {
_filter: {
_or: [
{ name: 'John' },
{ email: { regex: '@example.com', options: 'i' } },
{ age: { gte: 18 } }
]
},
_sort: { name: 'asc' },
_limit: 50
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const records = result.data;
console.log('Filtered records:', records);
Filter data using AND and OR combined
// Get records applying both OR and AND filters
const result = await totalumClient.crud.query('users', {
_filter: {
_or: [
{ role: 'admin' },
{ role: 'moderator' }
],
status: 'active' // AND this condition must be true
},
_sort: { name: 'asc' },
_limit: 50
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const records = result.data;
console.log('Filtered records:', records);
Filter using custom MongoDB aggregation query
const tableName = 'users';
// Custom MongoDB aggregation query as string
const customMongoDbAggregationQueryInString = `
Your custom MongoDB aggregation query in string format
For more info: https://docs.mongodb.com/manual/aggregation/
`;
const result = await totalumClient.filter.runCustomMongoAggregationQuery(tableName, customMongoDbAggregationQueryInString);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const results = result.data;
Get historic updates of a record
const recordId = 'record-id-123';
const result = await totalumClient.crud.getHistoricRecordUpdatesById(recordId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const historicUpdates = result.data;
console.log('Historic updates:', historicUpdates);
Functions for create download and manipulate files
Upload a file to Totalum (you can upload any file type)
1. Upload the file to Totalum
To scan a document, first you need to upload the file to Totalum. You can do this using the Totalum API directly or using the Totalum SDK.
1.1 Transform the file to a blob
If you are not using javascript, you will need to do this step using the language you are using. Search on internet how to transform a file to a blob in your language. Or also you can ask chatgpt to transform the following examples to your language.
Depending on the platform you are using, you will need to transform the file to a blob. Here are some examples:
From a file input (Frontend)
const fileInput = document.getElementById('fileInput');
const fileBlob = fileInput.files[0];From a file in storage (Backend)
const fs = require('fs');
const yourFilePath = 'your_file_path'; // example: /user/your_file.pdf
const fileBlob = fs.readFileSync(yourFilePath);From a remote file (Backend)
const response = await axios.get('your_file_url', { responseType: 'stream' });
const fileBlob = response.data;From a base64 string (Frontend/Backend)
// Convert base64 to binary
const binaryStr = atob(base64String);
const len = binaryStr.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
let fileBlob;
// Environment check: Node.js or Browser
if (typeof process === 'object' && process.version) {
// Node.js environment
// Convert Uint8Array to Buffer for Node.js usage
const buffer = Buffer.from(bytes.buffer);
// Here, 'buffer' can be used similarly to how you'd use a Blob in the browser
// Note: Direct Blob emulation isn't possible in Node.js, but Buffer is a close alternative for file handling
fileBlob = buffer;
} else {
// Browser environment
// Create a Blob from the Uint8Array
const blob = new Blob([bytes], { type: fileType });
fileBlob = blob;
}1.2 Upload the file to Totalum
Using Totalum SDK
const FormData = require('form-data'); // if you are using node.js
const fileName = 'your_file_name.png'; // replace 'your_file_name' with the name of your file, replace .png with the extension of your file
const file = yourFileBlob // your blob file created in the previous step
const formData = new FormData();
formData.append('file', file, fileName);
const result = await totalumClient.files.uploadFile(formData);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const fileNameId = result.data;Using Totalum API
const FormData = require('form-data'); // if you are using node.js
const fileName = 'your_file_name.png'; // replace 'your_file_name' with the name of your file, replace .png with the extension of your file
const file = yourFileBlob // your blob file created in the previous step
const formData = new FormData();
formData.append('file', file, fileName);
const result = await axios.post('https://api.totalum.app/files/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
'api-key': 'your api key here', // replace 'your api key here' with your api key
}
});
const fileNameId = result.data;
// If you want to link this file to a record, you need to add the fileNameId to the record property of type file
const tableName = 'documents';
const recordId = 'record-id-123';
const result2 = await totalumClient.crud.editRecordById(tableName, recordId, {
'attachmentFile': { name: fileNameId }
});
if (result2.errors) {
console.error('Error:', result2.errors.errorMessage);
return;
}
console.log('File linked to record successfully');
Remove a file from Totalum
// Remove a file from Totalum using the file name id
const fileNameId = 'your_file_name.png';
const result = await totalumClient.files.deleteFile(fileNameId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('File deleted successfully');
Get the download URL of a file
// Get a signed download URL for a file
const fileNameId = 'your_file_name.png';
// Optional: set expiration time
const options = {
// Default expiration is 128 hours. Set custom expiration in milliseconds from now
expirationTime: Date.now() + (128 * 60 * 60 * 1000) // 128 hours
};
const result = await totalumClient.files.getDownloadUrl(fileNameId, options);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
// result.data is an array like ["https://..."], extract the URL:
const fileUrl = result.data[0];
console.log('Download URL:', fileUrl);
Generate a PDF from a template
// Go to Totalum -> Configuration -> PDF Templates to get your template ID
const templateId = 'template-id-123';
// Variables will be replaced in your template using Handlebars
const variables = {
'customerName': 'John Doe',
'orderDetails': {
'orderId': 'ORD-12345',
'amount': 150.50,
'date': new Date(),
},
};
const fileName = 'invoice_12345.pdf';
// CAUTION: if you use the same name, the previous PDF will be overwritten
const result = await totalumClient.files.generatePdfByTemplate(templateId, variables, fileName);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const generatedFileName = result.data;
console.log('PDF generated:', generatedFileName);
// Link this PDF to a record
const tableName = 'invoices';
const recordId = 'invoice-record-123';
const result2 = await totalumClient.crud.editRecordById(tableName, recordId, {
'pdfFile': { name: generatedFileName }
});
Get OCR of an image (extract text from image)
// First upload the image to Totalum, then extract text from it
const fileNameId = 'uploaded_image.png';
const result = await totalumClient.files.ocrOfImage(fileNameId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const ocrResult = result.data;
console.log('Extracted text:', ocrResult.text);
console.log('Full details:', ocrResult.fullDetails);
Get OCR of a PDF (extract text from PDF)
// First upload the PDF to Totalum, then extract text from it
const fileNameId = 'uploaded_document.pdf';
const result = await totalumClient.files.ocrOfPdf(fileNameId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const ocrResult = result.data;
console.log('Extracted text:', ocrResult.text);
console.log('Full details:', ocrResult.fullDetails);
Scan an invoice (extract structured data from invoice)
// First upload the invoice file to Totalum
const fileName = 'invoice.png';
const file = yourFileBlob;
const formData = new FormData();
formData.append('file', file, fileName);
const uploadResult = await totalumClient.files.uploadFile(formData);
if (uploadResult.errors) {
console.error('Error:', uploadResult.errors.errorMessage);
return;
}
const fileNameId = uploadResult.data;
// Scan the invoice to extract structured data
const result = await totalumClient.files.scanInvoice(fileNameId);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const invoiceData = result.data;
const metadata = result.data.metadata;
console.log('Invoice data:', invoiceData);
console.log('Total amount:', invoiceData.totalAmountIncludingTaxes);
console.log('Invoice number:', invoiceData.complete_invoice_number);
console.log('Usage units:', metadata.usageUnits);
Scan a document (extract custom fields from document)
// First upload the document
const fileName = 'document.png';
const file = yourFileBlob;
const formData = new FormData();
formData.append('file', file, fileName);
const uploadResult = await totalumClient.files.uploadFile(formData);
if (uploadResult.errors) {
console.error('Error:', uploadResult.errors.errorMessage);
return;
}
const fileNameId = uploadResult.data;
// Define the JSON schema for properties you want to extract
const properties = {
"fecha": {
"type": "string",
"format": "date",
"description": "The date of the document"
},
"import": {
"type": "number",
"description": "The total amount"
},
"currency": {
"type": "string",
"enum": ["EUR", "USD", "GBP", "OTHER"],
"description": "Currency code"
}
};
const result = await totalumClient.files.scanDocument(fileNameId, properties);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const documentData = result.data;
const metadata = result.data.metadata;
console.log('Extracted data:', documentData);
console.log('OCR text:', metadata.ocrFullResultText);
console.log('Usage units:', metadata.usageUnits);
Create PDF from HTML
Create a PDF directly from custom HTML:
const htmlContent = `
<html>
<body>
<h1>Hello World</h1>
<p>This is a PDF created from HTML</p>
</body>
</html>
`;
const fileName = 'my-generated-pdf.pdf';
const result = await totalumClient.files.createPdfFromHtml({
html: htmlContent,
name: fileName
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const generatedFileName = result.data;
console.log('PDF generated:', generatedFileName);
// Link this PDF to a record
const tableName = 'documents';
const recordId = 'doc-record-123';
const result2 = await totalumClient.crud.editRecordById(tableName, recordId, {
'pdfFile': { name: generatedFileName }
});
Note: The HTML is automatically encoded to base64 by the SDK before sending it to the API. Works in both Node.js and browser environments.
Functions for send emails
Send a basic email
const emailPayload = {
to: ['[email protected]'],
subject: 'Your email subject',
html: '<h1>Hello</h1><p>This is the email content in HTML</p>',
};
const result = await totalumClient.email.sendEmail(emailPayload);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Email sent successfully');
console.log('Message ID:', result.data.messageId);
Send an email with all options
const emailPayload = {
to: ['[email protected]', '[email protected]'], // array of recipients
subject: 'Your email subject',
html: '<h1>Hello</h1><p>This is the email content in HTML</p>',
fromName: 'Your Company Name', // optional: custom sender name
cc: ['[email protected]'], // optional: carbon copy recipients
bcc: ['[email protected]'], // optional: blind carbon copy recipients
replyTo: '[email protected]', // optional: reply-to address
attachments: [ // optional: array of attachments (max 10, each up to 15MB)
{
filename: 'document.pdf',
url: 'https://example.com/path/to/document.pdf',
contentType: 'application/pdf' // optional
},
{
filename: 'image.png',
url: 'https://example.com/path/to/image.png',
contentType: 'image/png' // optional
}
]
};
const result = await totalumClient.email.sendEmail(emailPayload);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Email sent successfully');
console.log('Message ID:', result.data.messageId);
Send an email with files from Totalum storage
// First, get the download URL of the file uploaded to Totalum
const fileNameId = 'your_file_name.pdf';
const fileUrlResult = await totalumClient.files.getDownloadUrl(fileNameId);
if (fileUrlResult.errors) {
console.error('Error:', fileUrlResult.errors.errorMessage);
return;
}
const fileUrl = fileUrlResult.data;
// Then, send the email with the attachment
const emailPayload = {
to: ['[email protected]'],
subject: 'Email with attachment from Totalum',
html: '<h1>Hello</h1><p>Please find the attached document</p>',
attachments: [
{
filename: 'document.pdf',
url: fileUrl,
contentType: 'application/pdf'
}
]
};
const result = await totalumClient.email.sendEmail(emailPayload);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
console.log('Email sent successfully');
Important notes:
- Maximum attachments: 10 per email
- Maximum size per attachment: 15MB
- All attachments must be provided as valid HTTP/HTTPS URLs
- The SDK validates attachments before sending
Functions for call OpenAI API without need to have an OpenAI account
Create a completion (deprecated, use createChatCompletion instead)
// See OpenAI API docs: https://platform.openai.com/docs/api-reference/completions/create
const body = {
model: 'gpt-3.5-turbo-instruct',
prompt: 'Say hello in Spanish',
max_tokens: 50
};
const result = await totalumClient.openai.createCompletion(body);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const completion = result.data;
console.log('Completion:', completion);
Create a chat completion
// See OpenAI API docs: https://platform.openai.com/docs/api-reference/chat/create
const body = {
messages: [
{ content: 'You are a math specialist assistant', role: 'system' },
{ content: 'How do I solve a matrix?', role: 'user' }
],
model: 'gpt-3.5-turbo',
max_tokens: 150,
};
const result = await totalumClient.openai.createChatCompletion(body);
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const chatCompletion = result.data;
console.log('Response:', chatCompletion.choices[0].message.content);
console.log('Tokens used:', chatCompletion.usage.total_tokens);
Generate an image
const result = await totalumClient.openai.generateImage({
prompt: 'A beautiful sunset over the mountains',
fileName: 'sunset_image',
size: '1024x1024', // optional: '1024x1024', '1536x1024' (landscape), '1024x1536' (portrait), 'auto'
quality: 'low', // optional: 'low' (fastest), 'medium', 'high', 'auto'
output_format: 'png', // optional: 'png', 'jpeg', 'webp'
background: 'auto' // optional: 'transparent' (requires png), 'opaque', 'auto'
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const { fileName, imageUrl } = result.data;
console.log('Image file:', fileName);
console.log('Image URL:', imageUrl); // signed URL to directly access the image
Edit an image
Edit or transform existing images: change clothes, combine images, use as style reference, etc.
const result = await totalumClient.openai.editImage({
prompt: 'Change ONLY the clothing to a blue suit. Keep the face and background the same.',
imageUrls: ['https://example.com/photo.jpg'], // 1-16 image URLs
fileName: 'edited-photo',
input_fidelity: 'high', // 'high' preserves details (faces). 'low' allows creative freedom (style transfer).
size: '1024x1024', // optional
quality: 'low', // optional
output_format: 'png' // optional
});
if (result.errors) {
console.error('Error:', result.errors.errorMessage);
return;
}
const { fileName, imageUrl } = result.data;
console.log('Edited image:', fileName);
console.log('Image URL:', imageUrl);
