Official SDKs

Use a maintained client library instead of crafting HTTP requests by hand.

If you prefer not to deal with HMAC signing, request building and JSON parsing by hand, use one of the official SDKs. The SDK signs every request automatically and exposes typed methods for every endpoint.

Java

The Bizzlink Java SDK is published on Maven Central.

Add the dependency

Maven (pom.xml):

<dependency>
    <groupId>lu.vigasoft</groupId>
    <artifactId>bizzlink-sdk-java</artifactId>
    <version>0.3.1</version>
</dependency>

Gradle (build.gradle.kts):

implementation("lu.vigasoft:bizzlink-sdk-java:0.3.1")

Requirements: Java 25 or newer.

Minimal example

import lu.vigasoft.bizzlink.sdk.BizzlinkClient;
import lu.vigasoft.bizzlink.sdk.generated.api.PingApi;
import lu.vigasoft.bizzlink.sdk.generated.model.PingResponse;

public class Example {

    public static void main(String[] args) throws Exception {

        try (BizzlinkClient client = BizzlinkClient.builder()
                .apiToken(System.getenv("BIZZLINK_API_TOKEN"))
                .hmacSecret(System.getenv("BIZZLINK_HMAC_SECRET"))
                .build()) {

            PingResponse pong = client.api(PingApi::new).ping();
            System.out.println("Status: " + pong.getStatus());
        }
    }
}

That’s the whole authentication flow — the SDK reads your API token and HMAC secret once, then signs every outgoing request transparently. No need to compute timestamps, build signature payloads or set headers manually.

Sending a UBL XML document

If you already have a UBL 2.1 Invoice or Credit Note XML on disk, the SDK has a fluent helper that reads the file, signs the request and sends it through Peppol:

import lu.vigasoft.bizzlink.sdk.BizzlinkClient;
import lu.vigasoft.bizzlink.sdk.generated.api.DocumentApi;
import lu.vigasoft.bizzlink.sdk.requests.SendUblDocumentRequest;

import java.nio.file.Path;

try (BizzlinkClient client = BizzlinkClient.builder()
        .apiToken(System.getenv("BIZZLINK_API_TOKEN"))
        .hmacSecret(System.getenv("BIZZLINK_HMAC_SECRET"))
        .build()) {

    var response = client.api(DocumentApi::new)
            .sendUblDocument(new SendUblDocumentRequest()
                    .ublXml(Path.of("invoice.xml")));

    System.out.println("Document ID: " + response.getDocumentId());
}

The SendUblDocumentRequest class is a thin SDK convenience wrapper over the generated request envelope — it pre-builds the JSON:API data.type slot and exposes overloads for Path, InputStream and byte[], so you don’t have to read the file or wrap the envelope manually.

Sending a JSON invoice

If you don’t have a UBL XML yet and want the API to build it for you from a simplified JSON model:

import lu.vigasoft.bizzlink.sdk.BizzlinkClient;
import lu.vigasoft.bizzlink.sdk.generated.api.DocumentApi;
import lu.vigasoft.bizzlink.sdk.requests.CreateAndSendInvoiceRequest;

try (BizzlinkClient client = BizzlinkClient.builder()
        .apiToken(System.getenv("BIZZLINK_API_TOKEN"))
        .hmacSecret(System.getenv("BIZZLINK_HMAC_SECRET"))
        .build()) {

    var response = client.api(DocumentApi::new)
            .createAndSendInvoice(new CreateAndSendInvoiceRequest()
                    .attributes(inv -> inv
                            .invoiceNumber("INV-2026-001")
                            // ... see Schema Reference for the full shape
                    ));

    System.out.println("Document ID: " + response.getDocumentId());
}

See the Schema Reference for the complete InvoiceJsonRequest shape.

Convenience wrappers for every mutation endpoint

The lu.vigasoft.bizzlink.sdk.requests package contains hand-written subclasses of every generated *Request envelope so you don’t have to construct the JSON:API data.type + data.attributes boilerplate yourself. Available wrappers:

WrapperWrapsUse with
SendUblDocumentRequestUBL XML send (with Path / InputStream / byte[] overloads)DocumentApi.sendUblDocument(...)
CreateAndSendInvoiceRequestJSON invoice → UBL → PeppolDocumentApi.createAndSendInvoice(...)
CreateAndSendCreditNoteRequestJSON credit note → UBL → PeppolDocumentApi.createAndSendCreditNote(...)
CreateAndSendUblJsonV2RequestOASIS UBL-JSON v2 → PeppolDocumentApi.createAndSendUblJsonV2(...)
ValidateXmlRequestUBL XML validation onlyValidationApi.validateXml(...)
CreateWebhookRequest / UpdateWebhookRequestWebhook config CRUDWebhooksApi.createWebhook(...) / updateWebhook(...)
CreatePartnerTenantRequest / UpdatePartnerTenantRequestSub-tenant CRUD (Partner API)PartnerTenantsApi.*
CreateLegalEntityRequest / UpdateLegalEntityRequestLegal entity CRUD (Partner API)PartnerLegalEntitiesApi.*
CreatePeppolIdRequestPeppol ID assignment (Partner API)PartnerPeppolIdsApi.createPeppolId(...)
CreateNotificationEmailRequest / UpdateNotificationEmailRequestInbound notification configNotificationEmailsApi.*
CreateEmailTemplateRequest / UpdateEmailTemplateRequestEmail template CRUDEmailTemplatesApi.*

Each wrapper exposes:

  • attributes() — direct access to the inner DTO
  • attributes(Consumer<T>) — fluent lambda configuration
  • scope(JsonApiScope) — set the JSON:API ownership scope (use Scopes.tenant(), Scopes.legalEntity(peppolId), Scopes.partner())

Example with scope:

client.api(WebhooksApi::new).createWebhook(
        new CreateWebhookRequest()
                .scope(Scopes.legalEntity("9930:LU12345678"))
                .attributes(w -> w
                        .url("https://my.app/webhook")
                        .events(List.of("document.received"))));

Sandbox vs Production

The environment is encoded in the API token itself — there is one gateway URL (https://gateway.vigasoft.lu/bizzlink) and the gateway routes your request based on whether you authenticated with a sandbox or production key. No client-side configuration needed: paste a sandbox key for sandbox calls, paste a production key for production calls.

Storing your credentials

Your API token and HMAC secret are equivalent to a username and password — anyone holding them can issue invoices in your name. Treat them accordingly.

Never do this

  • Hardcode in source code. Even in a private repo, the credentials end up in git history forever and leak via stack traces, log lines, screen-shares and AI tools.
  • Commit .env or config files containing secrets. Add them to .gitignore from day one.
  • Embed in client-side code. Mobile apps, browser JavaScript and desktop binaries can be decompiled — anything shipped to a user device is public.
  • Share via email, chat or ticket systems. These channels are logged, archived and frequently breached.

For production workloads, store credentials in a dedicated secret manager and inject them at runtime as environment variables or process arguments:

PlatformService
AWSSecrets Manager, Parameter Store
AzureKey Vault
GCPSecret Manager
Self-hostedHashiCorp Vault, Bitwarden Secrets Manager, Infisical
KubernetesSealed Secrets, External Secrets Operator

The Java SDK examples above read from System.getenv("BIZZLINK_API_TOKEN") — the same pattern works whether the env var comes from a secret manager, a systemd unit file, a Docker --env-file, or a Kubernetes Secret.

Acceptable for local development

For local dev only, a .env file (gitignored) loaded via tooling such as direnv, dotenv-java or your IDE’s run-configuration is fine. Keep one separate set of credentials per developer — don’t share a single dev key across the team.

Spring Boot integration

If you use Spring Boot, externalize via application.yml:

bizzlink:
  api-token: ${BIZZLINK_API_TOKEN}
  hmac-secret: ${BIZZLINK_HMAC_SECRET}
@Configuration
public class BizzlinkConfig {

    @Bean(destroyMethod = "close")
    BizzlinkClient bizzlinkClient(
            @Value("${bizzlink.api-token}") String apiToken,
            @Value("${bizzlink.hmac-secret}") String hmacSecret) {

        return BizzlinkClient.builder()
                .apiToken(apiToken)
                .hmacSecret(hmacSecret)
                .build();
    }
}

Spring resolves ${BIZZLINK_API_TOKEN} from the environment, so the actual secret never lives in your repo.

Rotation

Rotate credentials at least once a year, immediately after any team member with access leaves, and immediately if a leak is suspected. Rotate via Settings → API Keys — generate a new key, deploy it, then revoke the old one.

Other languages

SDKs for other languages are on the roadmap. In the meantime, build directly against the OpenAPI specification — every official SDK is generated from this same spec.