---
title: "Get started on integration - Rest PKI Core"
description: "From nothing to a first working call to Rest PKI Core: get the endpoint and an API key, set up the client library or REST API, and make a first call."
canonical: "https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started"
locale: "en"
product: "Rest PKI Core"
last_updated: "2026-10-14"
---

# Get started on integration - Rest PKI Core

This article takes you from nothing to a first working call to [Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core). You will:

1. [Get your credentials](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#parameters): the endpoint of the instance and an API key
1. [Set up the client library](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#calling-the-api) for your programming language, or call the REST API directly
1. [Make a first call](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#first-call) to check that everything is in place

## How the integration works

Your application talks to Rest PKI Core through a REST API, authenticated by an API key. The calls are always made by your application's
**backend**: the API key gives access to your subscription, so it must never reach a browser or a mobile app.

When an operation needs the user, such as choosing a certificate and signing a document, or taking a selfie for a biometric check, you don't
have to build those pages. Your backend creates a **session**, Rest PKI Core returns a URL, and the user is sent to a page hosted by Rest PKI
Core. When the user is done, they come back to your application, which fetches the results through the API. See
[signature sessions](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/signature-sessions) and [biometric sessions](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/bio-sessions).

## Get your credentials

You need two parameters:

Parameter   | Description
----------- | -----------
**Endpoint** | Address of the Rest PKI Core instance. On the SaaS instance it is `https://core.pki.rest/`. On your own instance, it is the address of its dashboard, such as `https://restpki.yourcompany.com/`
**API key** | Key that identifies your application and authenticates its calls. It looks like `MyApp\|0f3c…`: a prefix taken from the application name, a `\|`, then 64 hexadecimal characters

If you are using the SaaS instance and your organization does not have access to it yet, contact our
[developer support](mailto:suporte@lacunasoftware.com). If you are using your own [on-premises instance](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/on-premises) and have no
subscription to work with yet, see [Creating a subscription](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/on-premises/create-sub).

There is no public sandbox. The [samples](https://github.com/LacunaSoftware/PkiSuiteSamples) and the demo site use a trial subscription on
the SaaS instance, whose API key Lacuna rotates every month: take the current key from the samples repository instead of copying it into
your code. 

### Create an API key

API keys belong to **applications**. Once you can sign in to the dashboard, create an application for your system and a key for it:

1. Sign in to the dashboard of the instance
1. On the left menu, click **Applications**, then **Add**
1. On **New Application**, fill in a **Name** for the application. If you are asked for a **Subscription**, choose the one your system will
   work on
1. Under **Roles**, check **Worker** (see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles) below)
1. Click **Create**
1. On the application's page, click **Keys**, then **Add**
1. On **New Application Key**, fill in a **Description** (for instance, the environment that will use the key) and choose an
   **Expiration** (see [Key expiration](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#key-expiration) below), then click **Create**
1. Copy the key shown on the **Key created** dialog and store it in a safe place. **You will not be able to view it again**: if you lose it,
   create a new key

> **Warning: Keep the API key secret**
>
> Anyone who has the key can act on your subscription with the roles of its application. Store it as you would store a password: in a secret
> store such as Azure Key Vault, in an environment variable or, during development, in .NET user secrets. Never commit it to source control and
> never send it to a browser or a mobile app.

#### Choosing roles

The roles of an application define what its keys can do. Give your application only the roles it needs:

Role        | What it allows
----------- | --------------
**Worker**  | The operations of an integration: create and use signature sessions, sign documents, inspect signatures, authenticate users with certificates, issue timestamps, run biometric sessions, add marks and stamps to documents, and view transactions. **This is the role most integrations need**
**Monitor** | View transactions, biometric sessions, enrollment batches and invoices, without performing any operation
**Manager** | Everything a Worker does, plus managing the subscription's settings and security contexts. It can view applications and users, but not manage them
**Admin**   | Everything, including managing applications and users

#### Key expiration

The **Expiration** of a key can be **In 1 year**, **In 2 years** or **Never expires**. When a key expires, the calls made with it start failing
with HTTP 401, so if you choose an expiring key, plan its replacement: an application can have more than one key, which lets you create the new
key and switch your system to it before the old one expires.

## Choose how to call the API

Rest PKI Core's APIs are plain REST with JSON, but you don't need to deal with HTTP yourself. Lacuna provides client libraries that wrap the
API in the idioms of each language, together with sample projects that use them:

Language | Package | Source code | Samples
-------- | ------- | ----------- | -------
.NET     | [Lacuna.RestPkiCore.Client](https://www.nuget.org/packages/Lacuna.RestPkiCore.Client/) on NuGet | — | [PkiSuiteSamples/dotnet](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/dotnet)
PHP      | [lacuna/restpkicore-client](https://packagist.org/packages/lacuna/restpkicore-client) on Packagist | [GitHub](https://github.com/LacunaSoftware/RestPkiNGPhpClient) | [PkiSuiteSamples/php](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/php/plain)
Java     | [com.lacunasoftware.restpki:restpkicore-client](https://central.sonatype.com/artifact/com.lacunasoftware.restpki/restpkicore-client) on Maven Central | [GitHub](https://github.com/LacunaSoftware/RestPkiNGJavaClient) | [PkiSuiteSamples/java](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/java/springmvc)
Node.js  | [restpki-core-client](https://www.npmjs.com/package/restpki-core-client) on npm | [GitHub](https://github.com/LacunaSoftware/RestPkiCoreNodeClient) | [PkiSuiteSamples/nodejs](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/nodejs/expressmvc)
Python   | [restpki-ng-python-client](https://pypi.org/project/restpki-ng-python-client/) on PyPI | — | [PkiSuiteSamples/python](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/python/flask)

Jump to the section of your language:

* [.NET](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#dotnet)
* [PHP](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#php)
* [Java](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#java)
* [Node.js](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#nodejs)
* [Python](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#python)

For any other language, [call the API directly](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#rest).

### .NET

Add the NuGet package [Lacuna.RestPkiCore.Client](https://www.nuget.org/packages/Lacuna.RestPkiCore.Client/) to your project:

```bash
dotnet add package Lacuna.RestPkiCore.Client
```

The package targets .NET Standard 2.0, so it runs on .NET 5 or later, on .NET Core 2.0 or later and on .NET Framework 4.6.1 or later
(Microsoft recommends 4.7.2 or later for .NET Standard 2.0 libraries).

> **Note**
>
> The package is named `Lacuna.RestPkiCore.Client`, but its classes are in the `Lacuna.RestPki.Client` namespace.

#### ASP.NET Core applications

Register the Rest PKI Core services in *Program.cs*, binding their options to the `RestPki` configuration section:

```cs
builder.Services.AddRestPki().Configure(builder.Configuration.GetSection("RestPki"));
```

Then add the section to *appsettings.json*, leaving the API key out of it:

```json
{
	"RestPki": {
		"Endpoint": "https://core.pki.rest/"
	}
}
```

Provide the API key through a source that stays out of source control. During development, use
[user secrets](https://learn.microsoft.com/aspnet/core/security/app-secrets):

```bash
dotnet user-secrets set "RestPki:ApiKey" "YOUR_API_KEY"
```

In production, set the environment variable `RestPki__ApiKey`, or use a secret store such as Azure Key Vault. ASP.NET Core merges these
sources into the same `RestPki` section.

Wherever your application needs to call Rest PKI Core, request an `IRestPkiService` through dependency injection:

```cs
using Lacuna.RestPki.Client;

public class SignatureController : Controller {

	private readonly IRestPkiService restPkiService;

	public SignatureController(IRestPkiService restPkiService) {
		this.restPkiService = restPkiService;
	}

	...
}
```

`AddRestPki()` also registers `IRestBioService`, the service used for [biometric sessions](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/bio-sessions).

> **Tip**
>
> If you prefer to set the options in code, pass them to `AddRestPki()`:
> `builder.Services.AddRestPki(options => { options.Endpoint = ...; options.ApiKey = ...; });`

#### Other .NET applications

If your application does not use ASP.NET Core dependency injection, as in a .NET Framework or console application, get an `IRestPkiService`
from the static class `RestPkiServiceFactory`:

```cs
using Lacuna.RestPki.Client;

var restPkiService = RestPkiServiceFactory.GetService(new RestPkiOptions() {
	Endpoint = ConfigurationManager.AppSettings["RestPkiEndpoint"],
	ApiKey = ConfigurationManager.AppSettings["RestPkiApiKey"],
});
```

In this example, the parameters come from the `appSettings` section of the *web.config* or *app.config* file, but they can come from wherever
you prefer. For biometric sessions, use `RestPkiServiceFactory.GetBioService()`, which takes the same parameters.

Each call to the factory creates a new HTTP client, so create the service once and reuse it, for instance in a static field, instead of creating
one on every request.

#### Options

`RestPkiOptions` has the following properties, which can also be set in the `RestPki` configuration section:

Property         | Description
---------------- | -----------
`Endpoint`       | Address of the Rest PKI Core instance. If the address has a path, end it with a slash (`https://yourcompany.com/restpki/`), otherwise the last segment of the path is dropped
`ApiKey`         | The application's API key
`CultureName`    | Culture of the API's responses, such as `en-US`, `pt-BR` or `es-ES` (see [Culture](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#dotnet-culture) below). Also available as `Culture`, of type `CultureInfo`
`TimeoutSeconds` | How long to wait for each response, in seconds. If not set, the client waits 60 seconds. Also available as `Timeout`, of type `TimeSpan`

#### Culture

The client tells Rest PKI Core which culture to use in each call, and by default it uses the culture of the current thread
(`Thread.CurrentThread.CurrentUICulture`). This works well when your application sets the culture of each request, as ASP.NET Core does with
request localization.

To always use the same culture regardless of the thread, set `CultureName`. In ASP.NET Core, add it to the configuration section:

```json
{
	"RestPki": {
		"Endpoint": "https://core.pki.rest/",
		"CultureName": "en-US"
	}
}
```

Otherwise, set it on `RestPkiOptions`:

```cs
var restPkiService = RestPkiServiceFactory.GetService(new RestPkiOptions() {
	...,
	CultureName = "en-US",
});
```

The same setting defines the language of the hosted biometric capture page (taken from the `Accept-Language` of the session-start request), so setting `CultureName` pins every capture page to that language.

#### Errors

The methods of `IRestPkiService` may throw the following exceptions:

Exception                  | When
-------------------------- | ----
`RestPkiExceptionV2`              | The API rejected the request (HTTP 422). Check the `ErrorCode` property, of type `ErrorCodesV2`, against the [error codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/error-codes). The `ErrorMessage` property describes the error in English and is meant for your logs, not for your users
`ValidationException`             | A certificate or signature failed validation. The `ValidationResults` property tells what failed
`RestForbiddenOperationException` | The application doesn't have a role that allows the operation (HTTP 403, see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles))
`RestErrorException`              | Any other HTTP error, such as 401 for a missing or invalid API key. The `StatusCode` property has the HTTP status code (see [HTTP status codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#http-status-codes))
`RestUnreachableException`        | The endpoint could not be reached: check the endpoint address and your network
`TimeoutException`                | The response took longer than `TimeoutSeconds`

`RestPkiExceptionV2` and `ValidationException` are in the `Lacuna.RestPki.Client` namespace, and the other `Rest…Exception` classes are in
the `Lacuna.RestClient` namespace. All of them derive from `Lacuna.RestClient.RestException`, so to handle any error from the API in a
single place, catch `RestException` (and `TimeoutException`, which is a standard .NET exception).

### PHP

Add the Composer package [lacuna/restpkicore-client](https://packagist.org/packages/lacuna/restpkicore-client) to your project (if you don't
have Composer yet, [get it here](https://getcomposer.org/)):

```bash
composer require lacuna/restpkicore-client
```

The package requires PHP 5.5 or later and uses Guzzle 6 or 7 to make HTTP calls. It is open source, hosted on
[GitHub](https://github.com/LacunaSoftware/RestPkiNGPhpClient): feel free to fork it if you need to customize it.

Create a `RestPkiService`, which implements `RestPkiServiceInterface`, passing it a `RestPkiCoreClient` built with the **endpoint** and the
**API key**:

```php
use Lacuna\RestPki\RestPkiOptions;
use Lacuna\RestPki\RestPkiCoreClient;
use Lacuna\RestPki\RestPkiService;

$options = new RestPkiOptions('https://core.pki.rest/', getenv('RESTPKI_API_KEY'));
$client = new RestPkiCoreClient($options);
$service = new RestPkiService($client);
```

In this example, the API key comes from the environment variable `RESTPKI_API_KEY`, which keeps it out of your code.

#### Culture

To set the culture of the API's responses, pass it as the third parameter of `RestPkiOptions`:

```php
$options = new RestPkiOptions('https://core.pki.rest/', getenv('RESTPKI_API_KEY'), 'en-US');
```

#### Errors

All of the following exceptions are in the `Lacuna\RestPki` namespace:

Exception                  | When
-------------------------- | ----
`RestPkiException`         | The API rejected the request (HTTP 422). `getErrorCode()` returns the [error code](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/error-codes) and `getDetail()` describes the error, for your logs
`ValidationException`      | A certificate or signature failed validation
`RestErrorException`       | Any other HTTP error. `getStatusCode()` returns the HTTP status code (see [HTTP status codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#http-status-codes))
`RestUnreachableException` | The endpoint could not be reached

#### PHP samples

In our [samples repository](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/php/plain/public), the folders whose names end
in `-rest-core` use Rest PKI Core. The endpoint and the API key are set in the `restPkiNg` entry of
[*config.php*](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/config.php).

### Java

Add the package `com.lacunasoftware.restpki:restpkicore-client`, from Maven Central, to your project. It requires Java 8 or later.

With Gradle, in *build.gradle*:

```groovy
repositories {
	mavenCentral()
}

dependencies {
	implementation 'com.lacunasoftware.restpki:restpkicore-client:1.4.0'
}
```

With Maven, in *pom.xml*:

```xml
<dependency>
	<groupId>com.lacunasoftware.restpki</groupId>
	<artifactId>restpkicore-client</artifactId>
	<version>1.4.0</version>
</dependency>
```

Check [Maven Central](https://central.sonatype.com/artifact/com.lacunasoftware.restpki/restpkicore-client) for the latest version.

> **Warning**
>
> Don't confuse it with `com.lacunasoftware.restpki:restpki-core-client`, an older package that is no longer updated.

The package is open source, hosted on [GitHub](https://github.com/LacunaSoftware/RestPkiNGJavaClient): feel free to fork it if you need to
customize it.

Get a `RestPkiService` from `RestPkiServiceFactory.getService()`, passing a `RestPkiOptions` with the **endpoint** and the **API key**:

```java
import com.lacunasoftware.restpkicore.RestPkiOptions;
import com.lacunasoftware.restpkicore.RestPkiService;
import com.lacunasoftware.restpkicore.RestPkiServiceFactory;

RestPkiOptions options = new RestPkiOptions();
options.setEndpoint("https://core.pki.rest/");
options.setApiKey(System.getenv("RESTPKI_API_KEY"));
RestPkiService service = RestPkiServiceFactory.getService(options);
```

In this example, the API key comes from the environment variable `RESTPKI_API_KEY`, which keeps it out of your code. If your application
reaches the internet through a proxy, set it with `options.setProxy()`.

#### Culture

To set the culture of the API's responses, call `setCulture()`:

```java
options.setCulture("en-US");
```

#### Errors

The methods of `RestPkiService` declare `throws Exception`. These are the exceptions they throw, all in the `com.lacunasoftware.restpkicore`
package:

Exception                       | When
------------------------------- | ----
`RestPkiException`              | The API rejected the request (HTTP 422). `getErrorCode()` returns the [error code](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/error-codes)
`ValidationException`           | A certificate or signature failed validation
`RestResourceNotFoundException` | The resource was not found (HTTP 404)
`RestErrorException`            | Any other HTTP error. `getStatusCode()` returns the HTTP status code (see [HTTP status codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#http-status-codes))
`RestUnreachableException`      | The endpoint could not be reached

#### Java samples

In our [samples repository](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/java/springmvc), the controllers whose names
end in `CoreController`, such as `SignatureSessionRestCoreController`, use Rest PKI Core. The endpoint and the API key are set under
`rest-pki-core` in *application.yml*.

### Node.js

Add the package [restpki-core-client](https://www.npmjs.com/package/restpki-core-client), from npm, to your project:

```bash
npm install restpki-core-client
```

The package doesn't declare the Node.js versions it supports. It brings its TypeScript typings, and makes its HTTP calls with the `request`
package, which is deprecated on npm. It is open source, hosted on [GitHub](https://github.com/LacunaSoftware/RestPkiCoreNodeClient).

Create a `RestPkiCoreClient`, passing it the **endpoint**, without the trailing slash, as `baseUrl` and the **API key** as `apiKey`:

```js
const { RestPkiCoreClient } = require('restpki-core-client');

const client = new RestPkiCoreClient({
	baseUrl: 'https://core.pki.rest',
	apiKey: process.env.RESTPKI_API_KEY,
});
```

In this example, the API key comes from the environment variable `RESTPKI_API_KEY`, which keeps it out of your code. The library sends it on
the `X-Api-Key` header and builds each URL by appending the route, such as `/api/signature`, to `baseUrl`.

The client has a method for each route, such as `prepareSignature()` for `POST api/signature`, `completeSignature()` for
`POST api/signature/completion` and `createSignatureSession()` for `POST api/signature-sessions`. Each method returns a promise of an object
whose `data` property holds the body of the response, and `statusCode` its HTTP status code:

```js
const response = await client.createSignatureSession({
	returnUrl: 'https://localhost:3000/signature-callback',
});

// Send the user to response.data.redirectUrl
```

> **Warning**
>
> The examples in the package's README create a `Configuration` object, a class that version 1.0.2 doesn't have: create a `RestPkiCoreClient`
> as above. The `timeout` and `ignoreSslErrors` options, which `RestPkiCoreClient` accepts, have no effect in this version.

#### Culture

The library sends no `Accept-Language` header. To set the culture of the API's responses, add it to `customHeaders`, which the library sends
on every request:

```js
const client = new RestPkiCoreClient({
	baseUrl: 'https://core.pki.rest',
	apiKey: process.env.RESTPKI_API_KEY,
	customHeaders: { 'Accept-Language': 'en-US' },
});
```

#### Errors

When the API answers with an HTTP status code other than 2xx, the promise is rejected with an `HttpError`, exported by the package, whose
`statusCode` property holds the HTTP status code (see [HTTP status codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#http-status-codes)) and `body` the body of the response, already
parsed from JSON. On HTTP 422, when the API rejected the request, `body` is an [ErrorModel](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#error-model). When the endpoint can't be reached,
the promise is rejected with the error of the `request` package.

#### Node.js samples

In our [samples repository](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/nodejs/expressmvc), the routes whose files end
in `-core.js`, such as *pades-signature-core.js*, use Rest PKI Core: they sign PDFs with [two-step signatures](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/two-step-signatures) and add
[PDF stamps](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/pdf-stamp). The endpoint and the API key are set under `restPkiCore` in *config/default.js*.

### Python

Add the package [restpki-ng-python-client](https://pypi.org/project/restpki-ng-python-client/), from PyPI, to your project:

```bash
pip install restpki-ng-python-client
```

The package doesn't declare the Python versions it supports. It depends on `pydantic` 2 and on `urllib3` below 2.1. Don't install its pre-releases
(`pip install --pre`): 0.9.2rc1 doesn't contain the library.

> **Note**
>
> The package is named `restpki-ng-python-client`, but you import it as `restpki_ng_python_client`.

The library is generated from the API: there is a class for each group of routes, such as `SignatureApi`, `SignatureSessionsApi`,
`DocumentsApi`, `SignatureInspectionApi` and `PdfApi`, and a method for each route, named after it, such as `api_signature_post()` for
`POST api/signature`. Create an `ApiClient` with the **endpoint**, without the trailing slash, and send the **API key** on the `X-Api-Key` header:

```python
import os
import certifi
from restpki_ng_python_client import ApiClient, Configuration, SignatureApi

configuration = Configuration(host='https://core.pki.rest', ssl_ca_cert=certifi.where())
client = ApiClient(configuration)
client.set_default_header('X-Api-Key', os.environ['RESTPKI_API_KEY'])

signature_api = SignatureApi(client)
```

In this example, the API key comes from the environment variable `RESTPKI_API_KEY`, which keeps it out of your code, and the trusted CAs for HTTPS
come from the [certifi](https://pypi.org/project/certifi/) package. The library builds each URL by appending the route to `host`, so an endpoint
ending in `/` gives URLs with `//`.

> **Warning**
>
> Don't use the `RestPkiClient` class of version 0.9.1.1: it turns off the verification of the server's TLS certificate.

#### Culture

The library sends no `Accept-Language` header. To set the culture of the API's responses, add it as a default header:

```python
client.set_default_header('Accept-Language', 'en-US')
```

#### Errors

All of the following exceptions are in the `restpki_ng_python_client.exceptions` module, and derive from `ApiException`:

Exception               | When
----------------------- | ----
`BadRequestException`   | HTTP 400: the request is malformed
`UnauthorizedException` | HTTP 401: the API key is missing, invalid or expired
`ForbiddenException`    | HTTP 403: the application doesn't have a role that allows the operation (see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles))
`NotFoundException`     | HTTP 404
`ServiceException`      | HTTP 500 or other 5xx
`ApiException`          | Any other HTTP error, including 422, when the API rejected the request. The library doesn't parse the [ErrorModel](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#error-model): read it as JSON from the `body` property, and the HTTP status code from `status`

#### Python samples

In our [samples repository](https://github.com/LacunaSoftware/PkiSuiteSamples/tree/master/python/flask), the view
*pades_signature_rest_core.py* uses Rest PKI Core, to sign a PDF with [two-step signatures](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/two-step-signatures). The endpoint and the API key are set in `REST_PKI_CORE_ENDPOINT` and
`REST_PKI_CORE_API_KEY`, in *sample/config.py*.

### Calling the API directly

The APIs of Rest PKI Core are RESTful, receiving and returning JSON, so they are easy to call from any modern programming language. If your
application uses a language without a client library, call them directly:

* **URL**: append the API's relative path to the **endpoint**. For instance, `api/signature-sessions` on the SaaS instance is
  `https://core.pki.rest/api/signature-sessions`
* **Authentication**: pass the **API key** on the `X-Api-Key` header of every request
* **Body**: send JSON with the `Content-Type: application/json` header

For instance:

```plaintext
POST https://core.pki.rest/api/signature-sessions
X-Api-Key: MyApp|0f3c...
Content-Type: application/json
```

All the APIs, with their requests and responses, are described on the [API reference](https://core.pki.rest/swagger). On your own instance,
the reference is at the path `swagger` of the endpoint, such as `https://restpki.yourcompany.com/swagger`.

#### HTTP status codes

The API responds with the following HTTP status codes:

Code                        | Meaning
--------------------------- | -------
200 (OK)                    | The request succeeded. The response depends on the API: see the [API reference](https://core.pki.rest/swagger)
400 (Bad Request)           | The request is malformed, for instance, a required field is missing
401 (Unauthorized)          | The API key is missing, invalid or expired
403 (Forbidden)             | The API key is valid, but its application doesn't have a role that allows the operation (see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles))
422 (Unprocessable Entity)  | The API rejected the request, usually because of a parameter that must be fixed. The body is an [ErrorModel](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#error-model)
500 (Internal Server Error) | An unexpected error happened. The `exceptionCode` field of the body helps diagnose the problem: include it when contacting support

#### ErrorModel

When the API responds with 422 (Unprocessable Entity), the body is an `ErrorModel`, named `ErrorModelV2` in the [API reference](https://core.pki.rest/swagger),
with the following fields:

Field               | Type     | Description
------------------- | -------- | -----------
`code`              | `string` | The error code (see [error codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/error-codes)). Write your error handling against this field
`message`           | `string` | Description of the error, in English. It is meant for your logs, not for your users, since it is not localized
`details`           | `object` | Additional information about the error, when there is any
`validationResults` | `object` | When the code is `ValidationError`, the results of the certificate or signature validation that failed

For instance:

```json
{
	"code": "SignatureSessionNotFound",
	"message": "Signature session not found: 00000000-0000-0000-0000-000000000000"
}
```

#### Culture

The API observes the `Accept-Language` request header, which changes the culture of the texts the API returns. The supported cultures are:

* `en-US` (or just `en`)
* `pt-BR` (or just `pt`)
* `es-ES` (or just `es`)

> **Note**
>
> Error messages are not affected by this header, so they should be logged, not shown to your users. To show an error to a user, map its `code`
> to a message of your own.

## Make a first call

With your credentials and the client library in place, check that everything works.

First, check the **endpoint**: open the address `api/system/info` of the endpoint in a browser, such as
[https://core.pki.rest/api/system/info](https://core.pki.rest/api/system/info). This address needs no API key and returns the version of the
instance:

```json
{"productName":"Lacuna Rest PKI Core","productVersion":"5.0.5 RTM","spaVersion":"...","timestamp":"..."}
```

Then, check the **API key** by creating a [signature session](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/signature-sessions). On .NET:

```cs
using Lacuna.RestPki.Api.SignatureSessions;

var response = await restPkiService.CreateSignatureSessionAsync(new CreateSignatureSessionRequest() {
	ReturnUrl = "https://localhost:5001/signature-callback",
});

// Send the user to response.RedirectUrl
```

Calling the API directly:

```bash
curl -X POST "https://core.pki.rest/api/signature-sessions" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "returnUrl": "https://localhost:5001/signature-callback" }'
```

The response brings the ID of the session and the URL of its signature page:

```json
{
	"sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
	"redirectUrl": "https://core.pki.rest/..."
}
```

Open the `redirectUrl` in a browser to see the page where your users will sign. If the call fails instead:

* **401**: the API key was not sent or is wrong. Check that you copied the whole key, including the application name before the `|`
* **403**: the application doesn't have the **Worker** role (see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles))
* **Endpoint unreachable**: check the address of the endpoint and whether your server can reach it

## Next steps

* [Signature sessions](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/signature-sessions): let your users sign documents on a page hosted by Rest PKI Core
* [Two-step signatures](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/two-step-signatures): sign on your own page with Web PKI, or with a key held by your server
* [Biometric sessions](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/bio-sessions): liveness, enrollment, facial authentication and identification, and ID capture
* [Webhooks](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/webhooks): get notified when a document is signed
* [Security contexts](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/security-contexts): choose which certificates are trusted
* [Signature inspection](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/signature-inspection): list and validate the signatures of a file
* [PDF stamps](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/pdf-stamp): add a document timestamp to a PDF
* [Referencing files](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/file-referencing): the ways to send documents to the API
* [Rest PKI client tool](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/client-tool): sign documents from the command line and from desktop applications
* [Requesting timestamps](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/requesting-timestamps): use Rest PKI Core as a timestamp provider
* [Error codes](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/error-codes): the codes the API may return

**Demos and samples**

- Live demo: [Your certificates in the browser](https://try.lacunasoftware.com/en/web-pki/certificates-in-the-browser)
- Live demo: [Sign a file with Rest PKI Core](https://try.lacunasoftware.com/en/pki-suite/sign-a-file-rest-pki-core)
- Authenticate with a digital certificate using Rest PKI Core: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/spa/PkiSuiteAspNetCoreAngularSample/PkiSuiteAspNetCoreAngularSample.Server/Controllers/AuthenticationRestPkiCoreController.cs) · [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/AuthenticationRestPkiCoreController.java)
- Sign a PDF with Rest PKI Core: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesSignatureRestCoreController.cs) · [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/PadesSignatureCoreController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/pades-signature-core.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/pades-signature-rest-core/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/pades_signature_rest_core.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/pades_signature_rest_core_controller.rb)
- Sign multiple PDFs with Rest PKI Core: [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/batch-pades-signature-core.js)
- Sign a PDF with a server key using Rest PKI Core: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesServerKeyRestCoreController.cs)
