Skip to content

.NET SDK

Amica.net

The Amica.net SDK allows a C#, F#, or VB developer to leverage our APIs with no need to write custom code to talk with them. It has been designed and carefully hand-crafted by our engineering team and it is used in production by our Amica Web Application and several other tools.

The code is open-source and developed in public on GitHub. The package is available on Nuget.

Keep your package updated

As our APIs evolve, so do our SDK, so make sure to frequently check Nuget for packages updates.

Installation

Installling the SDK is as simple as adding an Amica.net package reference to your project.

dotnet add package amica.net  --version 1.0.0
NuGet\Install-Package amica.net -Version 1.0.0
<PackageReference Include="amica.net" Version="1.0.0" />

If you use Visual Studio, JetBrains' Rider, or any other IDE, you may use your favorite IDE's UI to achieve the same goal. On this page, we use CLI examples as they are valid on any platform and are more concisely showcased.

Tutorial

We want to write a small app from scratch.

Our goals are:

  1. Create a new .NET project;
  2. Install Amica.net as a project dependency;
  3. Set both API Key and Application ID;
  4. Log in with a valid user (autenticate);
  5. Download the list of companies available to the user;
  6. Obtain some company's client/vendor records.

In this tutorial We will be using VS Code, but you can follow along with any editor or IDE of choice.

Create a new .NET project

In your terminal, start a new console project named Amica.net.Tutorial:

dotnet new console -o amica-dotnet-tutorial -n Amica.net.Tutorial

Then, cd into the newly created directory:

cd amica-dotnet-tutorial

Install the Amica.net package

Make sure you are in the amica-dotnet-tutorial directory, then type the follwoing command:

dotnet add package amica.net

Now, launch VSCode from within the current directory:

code .

The above command will open VSCode on the current project:

VS Code showing the newly created project

Tip

We assume that the VSCode C# Dev Kit is already installed. If not, please install it before continuing.

Set the API key and the App ID

Now, delete all the code in Program.cs and replace it with the following lines:

using Amica;

const string applicationId = "Amica.net.Tutorial";
var apiKey = Environment.GetEnvironmentVariable("AMICA_API_KEY") ?? "apikey";

Configuration.SetApiKey(apiKey);
Configuration.SetApplicationId(applicationId);
Imports Amica

Const applicationId = "Amica.net.Tutorial"
Dim apiKey as String = Environment.GetEnvironmentVariable("AMICA_API_KEY") or "apikey"

Configuration.SetApiKey(apiKey)
Configuration.SetApplicationId(applicationId)

As the API key is sensible information, we retrieve it from an environment variable. This way, its value won't be checked in in version control or appear anywhere in the codebase.

Tip

Setting environment variable values is a task achieved differently based on the operating system and IDE or editor you are using, which goes beyond this tutorial's scope. For VSCode, maybe check the answers to this Stack Overflow question.

Setting the Application ID is straightforward.

The Configuration class exposes some properties and methods such as SetApiKey and SetApplicationId that allow, you guessed it, proper SDK configuration. As the API key and the Application ID are required for most API requests, we set them immediately.

In VSCode, our code should now look like this (click to enlarge):

Amica.net is properly installed and configured

Now, hit F5 on the keyboard to launch the app and select "C#" as the debugger. The program should be built and then run, but nothing happens. That's OK; we only wanted to verify Amica.net is appropriately installed and configured.

Log in a user (authenticate)

Most API endpoints require an authentication token. This token is obtained by authenticating as a valid user. A user is authenticated by logging in with the user's credentials. Add the following code to the Program.cs file:

var userName = Environment.GetEnvironmentVariable("AMICA_USER") ?? "user";
var password = Environment.GetEnvironmentVariable("AMICA_PASSWORD") ?? "password";

var (loginResult, token) = await SessionManager.Login(userName, password);
if (!loginResult.IsSuccess)
{
    Console.WriteLine($"Login failed ({loginResult.HttpResponseMessage.StatusCode})");
    return;
}
Dim userName as String = Environment.GetEnvironmentVariable("AMICA_USER") or "user"
Dim password as String = Environment.GetEnvironmentVariable("AMICA_PASSWORD") or "password"

Dim loginResult = Await SessionManager.Login(userName, password)
If Not loginResult.response.IsSuccess Then
    Console.WriteLine($"Login failed ({loginResult.response.HttpResponseMessage.StatusCode})")
    Return
End If

Again, as the username and password are sensible information, we retrieve them from the environment.

The SessionManager.Login method allows us to validate user credentials and obtain the authentication token we'll use for all our requests on behalf of the user. Notice that the method returns a tuple. The first value is of Response<LoginResult> type, while the second is a nullable string that contains the authentication token we're interested in. If login fails, we report the failure and exit.

Tip

Most responses return a Response<T> or Response<List<T>> class instance. Response exposes several valuable properties, such as IsSuccess above or HttpResponseMessage that exposes the .NET HttpResponseMessage class instance for the request.

Once a token has been acquired, you can start working with the various API endpoints. In this tutorial, we will first obtain the list of companies (Azienda) available to the user, and then we'll download some clients and vendors from the first company on the list.

Download the companies' list

This SDK comprises several service classes, each dedicated to one API endpoint. Each service works with one particular data entity.

Since we now want to download the companies (Azienda) list, we must create an instance of the AziendaService class. Add the following line to Program.cs:

var aziendaService = new AziendaService();
Dim aziendaService As New AziendaService()

Once our service instance runs, we can invoke its List method to achieve our goal.

var requestSettings = new RequestSettings
{
    TokenAuthentication = new() { Token = token! }, 
    BaseAddress = loginResult.Result!.BaseAddress 
};

var aziendaResponse = await aziendaService.List(settings: requestSettings);
if (!aziendaResponse.IsSuccess)
{
    Console.WriteLine($"Companies 'List' request failed ({aziendaResponse.HttpResponseMessage.StatusCode})");
    return;
}

Console.WriteLine($"Available companies for user '{userName}:");
var companies = aziendaResponse.Result;
foreach (var company in companies!)
    Console.WriteLine(company.Nome, company.LicenseKey);
Dim requestSettings As New RequestSettings With {
    .TokenAuthentication = New TokenAuthentication With { .Token = loginResult.token }, 
    .BaseAddress = loginResult.response.Result.BaseAddress
    }

Dim aziendaResponse = Await aziendaService.List(settings := requestSettings)
If not aziendaResponse.IsSuccess Then
    Console.WriteLine($"Companies 'List' request failed ({aziendaResponse.HttpResponseMessage.StatusCode})")
    Return
End If

Console.WriteLine($"Available companies for user '{userName}:")
Dim companies = aziendaResponse.Result
for Each company in companies
    Console.WriteLine(company.Nome, company.LicenseKey)
Next
Configuration.RequestSettings.TokenAuthentication = new() { Token = token! };
Configuration.RequestSettings.BaseAddress = loginResult.Result!.BaseAddress;

var aziendaResponse = await aziendaService.List();
if (!aziendaResponse.IsSuccess)
{
    Console.WriteLine($"Companies 'List' request failed ({aziendaResponse.HttpResponseMessage.StatusCode})");
    return;
}

Console.WriteLine($"Available companies for user '{userName}:");
var companies = aziendaResponse.Result;
foreach (var company in companies!)
    Console.WriteLine(company.Nome, company.LicenseKey);
Configuration.RequestSettings.TokenAuthentication = New TokenAuthentication With { .Token = loginResult.token } 
Configuration.RequestSettings..BaseAddress = loginResult.response.Result.BaseAddress

Dim aziendaResponse = Await aziendaService.List()
If not aziendaResponse.IsSuccess Then
    Console.WriteLine($"Companies 'List' request failed ({aziendaResponse.HttpResponseMessage.StatusCode})")
    Return
End If

Console.WriteLine($"Available companies for user '{userName}:")
Dim companies = aziendaResponse.Result
for Each company in companies
    Console.WriteLine(company.Nome, company.LicenseKey)
Next

Thread-safe vs. thread-unsafe

Notice that we have two different code snippets: thread-safe and thread-unsafe.

In the thread-safe code snippet, we pass a RequestSettings instance to the List method. RequestSettings allows us to pass important information, such as the authentication token and the base address (BaseAddress) returned by the SessionManager.Login call we performed above. This approach requires us to pass a RequestSettings instance on every method call but has the advantage that calls can be concurrently executed.

In the thread-unsafe snippet, we don't pass a RequestSettings instance to the method call but rather set the equivalent static properties of the Configuration class. The List call is straightforward, and, more importantly, we set the token and base address once, and then we forget about it. The thread-unsafe approach can be used when performing single-thread (non-concurrent) API calls.

When the request is successful, the response's Result property always contains the requested items. If unsuccessful, Result will be null.

Always test for success

It is always a good idea to test whether a request has been successful by checking the Response.IsSuccess property. In case of failure, you can inspect Response.HttpResponseMessage and Response.ProblemDetails for hints on what went wrong.

Get a Anagrafiche list

Now, we can pick one company (the first in the list) and request a list of some of its clients. In this case, we leverage the AnagraficheService, responsible for working with Anagrafica entities.

Add the following code to Program.cs:

var targetCompany = companies[0];

requestSettings.CompanyId = targetCompany.Id;
requestSettings.TokenAuthentication.LicenseKey = targetCompany.LicenseKey;

Console.WriteLine($"Retrieving the first ten clients of company '{targetCompany.Nome}'...");

var service = new AnagraficaService();

var options = new ListOptions<Anagrafica> { Take = 50 };
options.AddFilter(x => x.Cliente, Operator.EqualTo, true);
options.AddSort(x => x.RagioneSociale1, Direction.Descending);
var anagraficaResponse = await service.List(options, settings: requestSettings);
if (!anagraficaResponse.IsSuccess)
{
    Console.WriteLine($"Anagrafiche 'List' request failed ({anagraficaResponse.HttpResponseMessage.StatusCode})");
    return;
}

foreach (var anagrafica in anagraficaResponse.Result!)
    Console.WriteLine(anagrafica.RagioneSociale1);
Dim targetCompany = companies(0)

requestSettings.CompanyId = targetCompany.Id
requestSettings.TokenAuthentication.LicenseKey = targetCompany.LicenseKey

Console.WriteLine($"Retrieving the first ten clients of company '{targetCompany.Nome}'...")

Dim service = New AnagraficaService()

Dim options = new ListOptions(Of Anagrafica) With{ .Take = 50 }
options.AddFilter(Function(x) x.RagioneSociale1, [Operator].EqualTo, true)
options.AddSort(Function(x) x.RagioneSociale1, Direction.Descending)
Dim anagraficaResponse = await service.List(options, settings:= requestSettings)
if (!anagraficaResponse.IsSuccess) Then
    Console.WriteLine($"Anagrafiche 'List' request failed ({anagraficaResponse.HttpResponseMessage.StatusCode})")
    Return
End If

For Each anagrafica in anagraficaResponse.Result
    Console.WriteLine(anagrafica.RagioneSociale1)
Next
var targetCompany = companies[0];

Configuration.RequestSettings.CompanyId = targetCompany.Id;
Configuration.RequestSettings.TokenAuthentication.LicenseKey = targetCompany.LicenseKey;

Console.WriteLine($"Retrieving the first ten clients of company '{targetCompany.Nome}'...");

var service = new AnagraficaService();

var options = new ListOptions<Anagrafica> { Take = 50 };
options.AddFilter(x => x.Cliente, Operator.EqualTo, true);
options.AddSort(x => x.RagioneSociale1, Direction.Descending);
var anagraficaResponse = await service.List(options);
if (!anagraficaResponse.IsSuccess)
{
    Console.WriteLine($"Anagrafiche 'List' request failed ({anagraficaResponse.HttpResponseMessage.StatusCode})");
    return;
}

foreach (var anagrafica in anagraficaResponse.Result!)
    Console.WriteLine(anagrafica.RagioneSociale1);
Dim targetCompany = companies(0)

Configuration.RequestSettings.CompanyId = targetCompany.Id
Configuration.RequestSettings.TokenAuthentication.LicenseKey = targetCompany.LicenseKey
Console.WriteLine($"Retrieving the first ten clients of company '{targetCompany.Nome}'...")

Dim service = New AnagraficaService()

Dim options = new ListOptions(Of Anagrafica) With{ .Take = 50 }
options.AddFilter(Function(x) x.RagioneSociale1, [Operator].EqualTo, true)
options.AddSort(Function(x) x.RagioneSociale1, Direction.Descending)
Dim anagraficaResponse = await service.List(options, settings:= requestSettings)
if Not anagraficaResponse.IsSuccess Then
    Console.WriteLine($"Anagrafiche 'List' request failed ({anagraficaResponse.HttpResponseMessage.StatusCode})")
    Return
End If

For Each anagrafica in anagraficaResponse.Result
    Console.WriteLine(anagrafica.RagioneSociale1)
Next

When we work with company data, we must set the RequestSettings.CompanyId and RequestSettings.TokenAuthentication.LicenseKey values.

What is a license key?

Every company has an associated license key you or your client received upon purchasing an Amica license. The license key is generally emailed a few minutes after the purchase on our website. The Azienda.LicenseKey property holds the company's license key.

Because we only want to download the first fifty records, we use the ListOptions.Take option on invoking the List method. Combined with the Skip option, Take allows us to perform data pagination as needed. ListOptions has other exciting features, such as Expand for expanding sub-objects, FilterBy for searches, and OrderBy for sorting. In the code above, we add one filter (records must have the Cliente (client) flag set to true) and sort by RagioneSociale1 descending. You can have multiple filters and sortings.

If our request is successful, we cycle through the resultset and print each company name (Anagrafica.RagioneSociale1) to the console.

Update a record

We want to add a note (Anagrafica.Note) to the first record. Add the following to Program.cs:

var client = anagraficaResponse.Result![0];

client.Note = "this is a note";

var resp = await service.Update(client, requestSettings);
if (!resp.IsSuccess)
{
    Console.WriteLine($"Update failed ({anagraficaResponse.HttpResponseMessage.StatusCode})");
    return;
}
Console.WriteLine("Update success!");
Console.WriteLine("Exiting now...");
Dim client = anagraficaResponse.Result(0)

client.Note = "this is a note"

Dim resp = Await service.Update(client, requestSettings)
if Not resp.IsSuccess Then
    Console.WriteLine($"Update failed ({anagraficaResponse.HttpResponseMessage.StatusCode})")
    Return
End If
Console.WriteLine("Update success!")
Console.WriteLine("Exiting now...")
var client = anagraficaResponse.Result![0];

client.Note = "this is a note";

var resp = await service.Update(client);
if (!resp.IsSuccess)
{
    Console.WriteLine($"Update failed ({anagraficaResponse.HttpResponseMessage.StatusCode})");
    return;
}
Console.WriteLine("Update success!");
Console.WriteLine("Exiting now...");
Dim client = anagraficaResponse.Result(0)

client.Note = "this is a note"

Dim resp = Await service.Update(client)
if Not resp.IsSuccess Then
    Console.WriteLine($"Update failed ({anagraficaResponse.HttpResponseMessage.StatusCode})")
    Return
End If
Console.WriteLine("Update success!")
Console.WriteLine("Exiting now...")

Wrapping up

We learned to correctly set up Amica.net and use API endpoints in read and write operations. We learned that multiple service classes exist in the SDK, approximately one per every API endpoint (or entity), and we can use those instances to interact with the remote API. We didn't use all the service methods, but the remaining ones (Add, Delete, Get) are used similarly to the ones seen above. There are other utility classes and services in the SDK, but those are out of the scope of a getting-started tutorial.

You understand the .NET SDK design, are confident enough to use Amica.net and can start using it to consume Amica APIs.

API Reference

The complete .NET SDK API reference is available here.