Using the Gini Bank API Library

The Gini Bank API Library uses kotlin coroutine suspend functions for a convenient and elegant way to work with asynchronous Gini Bank API requests. All the public suspend functions are main-safe, meaning they’re safe to call from the main thread.

Each suspend function returns an instance of the Resource sealed class. Depending on the request result the following Resource instances are returned:

  • successful request: Resource.Success which contains the deserialized response payload in the data property.
  • failed request: Resource.Error which contains the response details and/or the exception which caused the failure.
  • cancelled request: Resource.Cancelled.

Resource also provides a helper instance method for chaining requests called mapSuccess(). For more details please consult the reference documentation.

Upload a document

As the key aspect of the Gini Bank API is to provide information extraction for analyzing documents, the API is mainly built around the concept of documents. A document can be any written representation of information such as invoices, reminders, contracts and so on.

The Gini Bank API Library supports creating documents from images, PDFs or UTF-8 encoded text. Images are usually a picture of a paper document which was taken with the device’s camera.

The following example shows how to create a new document from a byte array containing a JPEG image.

// Assuming that `giniBankApi` is an instance of the `GiniBankAPI` facade class and `imageBytes`
// is an instance of a byte array containing a JPEG image,
// e.g. from a picture taken by the camera

coroutineScope.launch {
    // Create a partial document by uploading the document data
    val partialDocumentResource =
        giniBankApi.documentManager.createPartialDocument(imageBytes, "image/jpeg", "myFirstDocument.jpg")

    when (partialDocumentResource) {
        is Resource.Success -> {
            // Use the partial document
            val partialDocument = extractionsResource.data
        }
        is Resource.Error -> // Handle error
        is Resource.Cancelled -> // Handle cancellation
    }
}

Each page of a document needs to uploaded as a partial document. In addition documents consisting of one page also should be uploaded as a partial document.

Note

PDFs and UTF-8 encoded text should also be uploaded as partial documents. Even though PDFs might contain multiple pages and text is “pageless”, creating partial documents for these keeps your interaction with the library consistent for all the supported document types.

Extractions are not available for partial documents. Creating a partial document is analogous to an upload. For retrieving extractions see Getting extractions.

Note

The filename (myFirstDocument.jpg in the example) is not required, it could be null, but setting a filename is a good practice for human readable document identification.

Setting the document type hint

To easily set the document type hint we introduced the DocumentType enum. It is safer and easier to use than a String. For more details about the document type hints see the Document Type Hints in the Gini Bank API documentation.

Getting extractions

After you have successfully created the partial documents, you most likely want to get the extractions for the document. Composite documents consist of previously created partial documents. You can consider creating partial documents analogous to uploading pages of a document and creating a composite document analogous to processing those pages as a single document.

Before retrieving extractions you need to create a composite document from your partial documents. The createCompositeDocument() method accepts either a List of partial Documents or a LinkedHashMap. The LinkedHashMap contains partial Documents as keys and the user applied rotation as values. In both cases the order is important and the partial documents should be in the same order as the pages of the scanned document.

Gini needs to process the composite document first before you can fetch the extractions. Effectively this means that you won’t get any extractions before the composite document is fully processed. The processing time may vary, usually it is in the range of a couple of seconds, but blurred or slightly rotated images are known to drasticly increase the processing time.

The BankApiDocumentManager provides the getAllExtractionsWithPolling method which can be used to fetch the extractions after the processing of the document is completed. The following example shows how to achieve this in detail.

// Assuming that `giniBankApi` is an instance of the `GiniBankAPI` facade class and `partialDocuments` is
// a list of `Documents` which were returned by `createPartialDocument(...)` calls

coroutineScope.launch {
    // Create a partial document by uploading the document data
    val extractionsResource =
        giniBankApi.documentManager.createCompositeDocument(partialDocuments)
            .mapSuccess { compositeDocumentResource ->
                // Poll the document and retrieve the extractions
                giniBankApi.documentManager.getAllExtractionsWithPolling(compositeDocumentResource.data)
            }

    when (extractionsResource) {
        is Resource.Success -> {
            // You may use the extractions to fulfill your use-case
            val extractionsContainer = extractionsResource.data
            val amountToPay: SpecificExtraction? =
                extractionsContainer.specificExtractions["amountToPay"]
            val lineItems: CompoundExtraction? =
                extractionsContainer.compoundExtractions["lineItems"]
        }
        is Resource.Error -> // Handle error
        is Resource.Cancelled -> // Handle cancellation
    }
}

Sending feedback

Depending on your use case your app probably presents the extractions to the user and offers the opportunity to correct them. We do our best to prevent errors. You can help improve our service if your app sends feedback for the extractions Gini delivered. Your app should send feedback only for the extractions the user has seen and accepted. Feedback should be sent for corrected extractions and for correct extractions. The code example below shows how to correct extractions and send feedback.

Note

We also provide a sample test case here to verify that extraction feedback sending works. You may use it along with the example pdf and json files as a starting point to write your own test case.

The sample test case is based on the Bank API documentation’s recommended steps for testing extraction feedback sending.

// Assuming that `giniBankApi` is an instance of the `GiniBankAPI` facade class

coroutineScope.launch {
    val retrievedExtractions: ExtractionsContainer // provided
    val compositeDocument: Document // provided

    // amounToPay was wrong, we'll correct it
    val amountToPay: SpecificExtraction = retrievedExtractions.specificiExtractions["amountToPay"];
    amountToPay.value = "31.00:EUR";

    // we should send only feedback for extractions we have seen and accepted
    // all extractions we've seen were correct except amountToPay
    val feedback: Map<String, SpecificExtraction> = mutableMapOf(
        "iban" to retrievedExtractions.specificiExtractions["iban"],
        "amountToPay" to amountToPay,
        "paymentRecipient" to retrievedExtractions.specificiExtractions["paymentRecipient"],
        "paymentReference" to retrievedExtractions.specificiExtractions["paymentReference"],
    );

    val feedbackResource = giniBankApi.documentManager.sendFeedbackForExtractions(document, feedback);
}

Handling errors

Errors are returned via Resource.Error instances which contain the response details and/or the exception which caused the failure. You can use these to log the error and decide whether to allow the user to retry the request or not.

Debugging

You can enable the debugging mode by passing true to the GiniBankAPIBuilder.setDebuggingEnabled(). This will cause all requests and responses to be logged.