---
title: "CloudHub quick reference"
description: "CloudHub on one page: get an API key, install a client, create a session, send the user to a provider, read the certificate, sign a hash and handle errors."
canonical: "https://docs.lacunasoftware.com/en-us/articles/cloudhub/quick-reference"
locale: "en"
product: "CloudHub"
last_updated: "2026-10-12"
---

# CloudHub quick reference

The essentials of CloudHub on one page, with links to the full articles.

## What it is for

CloudHub connects your application to the ICP-Brasil cloud certificate providers (the PSCs, *Prestadores de Serviço de Confiança*, such as BirdID, SafeID and VIDaaS) through a single REST API and one API key. It finds which providers hold a certificate for a CPF or CNPJ, brokers the user's authorization at the chosen provider, and then reads the certificate and signs hashes with it. 
CloudHub does not build signed documents and does not validate certificates: a signature library (PKI SDK, Rest PKI, Rest PKI Core or PKI Express) computes the hash to sign, assembles the signed PDF, CMS or XML, and validates the certificate. CloudHub receives only the hash, and the private key never leaves the provider. 

| Choose instead | When it fits better |
| --- | --- |
| [PKI SDK](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/quick-reference) with `BrazilTrustServices` | Your backend is .NET and you will register your application with each provider yourself: the package talks to the providers directly, with a ClientID and ClientSecret per provider. |
| [Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/quick-reference) | You want a hosted signing page: a Rest PKI Core signature session lets users sign with certificates on their computer, through Web PKI, or with cloud certificates, and can accept cloud certificates only. |
| [Web PKI](https://docs.lacunasoftware.com/en-us/articles/web-pki/quick-reference) | The certificates are on the user's computer, token or smart card, not at a cloud provider. |

## Install

| Language | Package | Current major | Registry |
| --- | --- | --- | --- |
| C# / .NET | `Lacuna.Cloudhub.Client` | 1 (latest 1.0.0) | https://www.nuget.org/packages/Lacuna.Cloudhub.Client |
| C# / .NET, ASP.NET Core dependency injection (optional) | `Lacuna.Cloudhub.ClientCore` | 1 (latest 1.1.0) | https://www.nuget.org/packages/Lacuna.Cloudhub.ClientCore |
| PHP | `lacuna/cloudhub-client` | 2 (latest 2.0.2) | https://packagist.org/packages/lacuna/cloudhub-client |
| Node.js | `cloudhub-client` | 1 (latest 1.0.1) | https://www.npmjs.com/package/cloudhub-client |
| Java | `com.lacunasoftware.cloudhub:cloudhub-client` | 1 (latest 1.0.5) | https://central.sonatype.com/artifact/com.lacunasoftware.cloudhub/cloudhub-client |
| Python | none: call the REST API with `requests` | n/a | n/a |

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

The PHP client needs PHP 7.4 or later and uses Guzzle; its methods end in `Async` but are synchronous. The API has eight operations and one header, so any HTTP client works as well as a client library. See [Get started](https://docs.lacunasoftware.com/en-us/articles/cloudhub/get-started#2-install-a-client). 

## Configuration and authentication

Every call needs your **API key** in the `X-Api-Key` header; the address is `https://cloudhub.lacunasoftware.com`. A request without a valid key gets HTTP 401 with an empty body. 

```csharp
using Lacuna.Cloudhub.Api;
using Lacuna.Cloudhub.Client;

var cloudhub = new CloudhubClient(
    "https://cloudhub.lacunasoftware.com/",
    Environment.GetEnvironmentVariable("CLOUDHUB_API_KEY"));
```

- **Get a key** from Lacuna Software support (suporte@lacunasoftware.com), listing the redirect URIs your application will use in production and in development. 
- **Keep the key on your server.** It authorizes every call, including signing, so it must never reach a browser or a mobile app; if it leaks, ask support for a new one. 
- **Redirect URIs must be registered** for your key: an exact URI (query string included), a scheme-host-port prefix such as `https://app.example.com`, or a wildcard subdomain such as `https://*.example.com`. If you add your own query parameters, register a host or wildcard entry. See [Redirect URIs](https://docs.lacunasoftware.com/en-us/articles/cloudhub/how-it-works#redirect-uris). 
- With the Java client, leave out the trailing slash of the address (the client appends `/api/...`); the PHP client needs it. Don't share one Java `CloudhubClient` between threads that call it at the same time. 

## Core operations

A signature takes three calls: create a session, read the certificate when the user comes back, and sign the hash your signature library computed. The [REST API](https://docs.lacunasoftware.com/en-us/articles/cloudhub/rest-api) page documents every operation; [Get started](https://docs.lacunasoftware.com/en-us/articles/cloudhub/get-started) has each step in C#, PHP, Python, Node.js and Java. 

### Create a session

`POST /api/sessions` with the signer's CPF or CNPJ (digits only), the redirect URI and the session type. CloudHub asks every provider in parallel and returns one `authUrl` per provider that holds a certificate for that identifier; an empty `services` list means none does, or none answered in time. Without an identifier, it returns every provider and the user identifies themselves at the provider. 

```csharp
var session = await cloudhub.CreateSessionAsync(new SessionCreateRequest {
    Identifier = "12345678909",
    RedirectUri = "https://app.example.com/sign/complete?fileId=42",
    Type = TrustServiceSessionTypes.SingleSignature,
    LifetimeInSeconds = 300,
});

foreach (var service in session.Services) {
    Console.WriteLine($"{service.ServiceInfo.Provider}: {service.AuthUrl}");
}
```

| Session type | The user authorizes |
| --- | --- |
| `SingleSignature` (default) | One signature: one document, or a challenge to sign the user in. |
| `SignatureSession` | Any number of signatures until the session expires: several documents with one authorization. |
| `MultiSignature` | One signature operation covering one or more hashes; provider-dependent. CloudHub signs one hash per call, so prefer `SignatureSession`. |
| `AuthenticationSession` | Only reading the certificate. Not recommended: most providers don't support it. |

`lifetimeInSeconds` is a request: the provider may grant less, and applies its own default when you omit it. To carry your own data through the round trip, add query parameters to the redirect URI (they come back next to `session`), or send `customState`, which the user can't read or alter and which `GET /api/sessions/custom-state` returns; the published .NET and Java clients don't have that field. 

### Send the user to a provider

Show one choice per entry of `services`, with `badgeUrl` as the logo (it may be null) and `provider` as the caption, and navigate the browser to the chosen `authUrl`. The user approves at the provider, typically in its app, with a PIN or a QR code; the provider returns to CloudHub, which redirects to your redirect URI with a `session` query parameter appended. 

```text
https://app.example.com/sign/complete?fileId=42&session=9f2c…e81a
```

When `session` is missing, the user cancelled, the provider refused or the exchange failed; CloudHub does not say which, so let the user try again. Treat the `session` value (64 hexadecimal characters) as a credential: use it right away, keep it out of your logs and accept it only on the redirect URI you created the session with. 

### Read the certificate

`GET /api/sessions/certificate?session=…` returns the certificate, DER-encoded and then base64-encoded, as a JSON string; `GET /api/v2/sessions/certificate` adds its `alias` and the provider's `serviceName`. When the provider holds several certificates for the user, CloudHub picks the currently valid one whose validity started most recently; pass `alias` to `sign-hash` as `certificateAlias` to sign with exactly that certificate. 

```csharp
byte[] certificate = await cloudhub.GetCertificateAsync(session);
```

Before signing, compare the CPF (or CNPJ) in the certificate with the one you asked for: the user may have signed in at the provider with another account. Reading the certificate does not count as a signature, so a `SingleSignature` session can still sign once. 

### Sign a hash

`POST /api/sessions/sign-hash` with the session, the hash (base64) and its digest algorithm, by OID (`digestAlgorithmOid`, which takes precedence) or by name (`digestAlgorithm`, such as `SHA-256`). It returns the signature value, base64-encoded, for your signature library to embed. Signing waits on the provider: allow up to two minutes. 

```csharp
byte[] signature = await cloudhub.SignHashAsync(new SignHashRequest {
    Session = session,
    Hash = toSignHash,                  // the hash, as bytes
    DigestAlgorithmOid = digestAlgorithmOid,
});
```

The Java 1.x client returns the raw JSON string, quotes included, from the certificate and sign-hash operations: strip the quotes before base64-decoding, as [Get started](https://docs.lacunasoftware.com/en-us/articles/cloudhub/get-started#6-read-the-certificate-when-the-user-comes-back) shows. 

### Sign a PDF end to end

Two handlers: **discover** takes the CPF, creates the session and lists the providers; **complete**, the redirect URI, reads the certificate, has the signature library start the signature and return the hash to sign, sends the hash to `sign-hash`, and has the library finish the PDF. [Signing a PDF](https://docs.lacunasoftware.com/en-us/articles/cloudhub/pades-signature) has the complete PAdES example with PKI SDK (C#), Rest PKI (PHP, Node.js, Java) and PKI Express (Python). For several documents with one authorization, use `SignatureSession` and repeat the last three steps per document; CAdES and XAdES follow the same flow. 

### Sign a user in

Create a `SingleSignature` session (not `AuthenticationSession`), read the certificate, have CloudHub sign a nonce your signature library created, and have the library check the signature and validate the certificate; then find the user by the certificate's CPF. The certificate alone is not proof. See [Authentication](https://docs.lacunasoftware.com/en-us/articles/cloudhub/authentication). 

### Discover providers without a session

`GET /api/sessions/services?identifier=…` runs discovery without creating a session (no `authUrl`); `GET /api/sessions/services/{name}/availability?identifier=…` asks one provider; `POST /api/sessions/services/{name}` creates a session at the provider the user already chose, where `name` is a `serviceName` such as `BirdID`. See [Discovery](https://docs.lacunasoftware.com/en-us/articles/cloudhub/how-it-works#discovery). 

## Errors

CloudHub refuses a request with HTTP 422 and a JSON body with `code`, `message` and sometimes `details`. HTTP 400 lists invalid fields (such as a missing `redirectUri`), 401 means a missing or unknown API key, and 500 carries a short code to quote to Lacuna support. See [Errors](https://docs.lacunasoftware.com/en-us/articles/cloudhub/rest-api#errors). 

- **`SessionExpired`**: the session's lifetime is over, possibly on the first call if the user took long to authorize. Create a new session and send the user through the provider again. 
- **`SessionInvalidOrAlreadyUsed`**: CloudHub doesn't know the session, or the provider no longer accepts it. Create a new session. 
- **`UnauthorizedRedirectUri`** (`details.redirectUri`) and **`InvalidRedirectUri`**: the redirect URI is not registered for your key, or is not absolute. Ask support to register it, or send a full URI. 
- **`IdentifierRequiredToDiscover`**, **`ServiceNotFound`**, **`DigestAlgorithmNameOrOidIsRequired`** and **`InvalidDigestAlgorithm`**: fix the request as the [error table](https://docs.lacunasoftware.com/en-us/articles/cloudhub/rest-api#errors) says. 
- **Reading the code in a client**: in .NET, catch `CloudhubApiException` and compare `CodeStr` (the `Code` enum is not filled in); `cloudhub-client` 1.x for Node.js throws a string that contains the JSON error; in PHP, decode the body of Guzzle's `ClientException`; in Java, check `ApiException.getCode() == 422` and its response body. 

## Test certificates and credentials

- CloudHub signs with cloud certificates held at the providers; the [Lacuna Test PKI certificates](https://docs.lacunasoftware.com/en-us/articles/pki-guide/test-certs) are files you install locally, not cloud certificates, and that page has no CloudHub section. 
- For local development, ask support to register a redirect URI such as `https://localhost:5001` next to your production ones. 
- The examples trust ICP-Brasil certificates only; to test with a provider's test certificates, trust their root in your signature library in development only. 
- The PKI Suite Samples come with a CloudHub API key in their configuration (in the .NET sample, `CloudHubApiKey` in `Web.config`); if the CloudHub sample doesn't work, the sample asks you to contact support. 

TODO(docs): whether CloudHub has a test environment or test providers, which providers issue test cloud certificates, and which redirect URIs the samples' API key accepts.

## Try it

Run the demo, or open the sample project for your language:

**Demos and samples**

- Live demo: [Sign a PDF with your cloud certificate](https://try.lacunasoftware.com/en/pki-suite/sign-with-cloud-certificate)
- Sign a PDF with a cloud certificate using CloudHub: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesCloudHubSdkController.cs) · [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/PadesSignatureCloudhubRestController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/pades-signature-cloudhub-restpki.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/pades-signature-cloudhub-rest/index.php)
- Sign an XML document with a cloud certificate using CloudHub: [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/xml-signature-cloudhub-rest/index.php)
- Authenticate with a cloud certificate using CloudHub: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/AuthenticationCloudHubSdkController.cs) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/authentication-cloudhub.js)
