Quick Start

Send your first PEPPOL invoice in under 5 minutes.

This guide walks you through sending your first invoice via the Bizzlink API.

Prerequisites

See Authentication for details on how to sign your requests.

Step 1: Verify Connectivity

The snippets below read BIZZLINK_API_TOKEN and BIZZLINK_HMAC_SECRET from environment variables so credentials never end up in code.

In production, inject them through your platform’s secret manager (AWS Secrets Manager, GCP Secret Manager, Vault, Kubernetes Secrets, 1Password CLI, …).

Signing payload format: {timestamp}.{method}.{path-and-query}.{sha256-hex(body)}

  • path-and-query — the full request path including the query string verbatim (/api/v1/documents?status=PENDING for a GET with query params, or just /api/v1/ping for none).
  • sha256-hex(body) — the body is hashed first, then the hex digest goes into the payload. For GET requests with no body, hash the empty string → e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.

See Authentication for the full spec.

# Fail fast if credentials are missing
: "${BIZZLINK_API_TOKEN:?BIZZLINK_API_TOKEN not set}"
: "${BIZZLINK_HMAC_SECRET:?BIZZLINK_HMAC_SECRET not set}"

TIMESTAMP=$(date +%s)
# Append ?key=value&... here if your endpoint takes query params
PATH_AND_QUERY="/bizzlink/api/v1/ping"

# GET has no body → hash the empty string
BODY_HASH=$(printf '' | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf '%s' "${TIMESTAMP}.GET.${PATH_AND_QUERY}.${BODY_HASH}" | \
  openssl dgst -sha256 -hmac "${BIZZLINK_HMAC_SECRET}" | sed 's/^.* //')

curl -X GET "https://gateway.vigasoft.lu${PATH_AND_QUERY}" \
  -H "Authorization: Bearer ${BIZZLINK_API_TOKEN}" \
  -H "X-Bizzlink-Signature: t=${TIMESTAMP},v1=${SIGNATURE}"
// Maven: lu.vigasoft:bizzlink-sdk-java:0.3.1
// SDK takes care of HMAC signing, query-string inclusion and body hashing.
import lu.vigasoft.bizzlink.sdk.BizzlinkClient;
import lu.vigasoft.bizzlink.api.PingApi;
import lu.vigasoft.bizzlink.model.PingResponse;

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("Connected: tenant=" + pong.getTenantName());
}
import hashlib
import hmac
import os
import time
import requests

API_BASE = "https://gateway.vigasoft.lu"
# Append ?key=value&... here if your endpoint takes query params
PATH_AND_QUERY = "/bizzlink/api/v1/ping"
API_TOKEN = os.environ["BIZZLINK_API_TOKEN"]
HMAC_SECRET = os.environ["BIZZLINK_HMAC_SECRET"]

timestamp = int(time.time())
# GET has no body → hash the empty string
body_hash = hashlib.sha256(b"").hexdigest()
payload = f"{timestamp}.GET.{PATH_AND_QUERY}.{body_hash}".encode()
signature = hmac.new(
    HMAC_SECRET.encode(), payload, hashlib.sha256
).hexdigest()

response = requests.get(
    f"{API_BASE}{PATH_AND_QUERY}",
    headers={
        "Authorization": f"Bearer {API_TOKEN}",
        "X-Bizzlink-Signature": f"t={timestamp},v1={signature}",
    },
)
print(response.json())
// Node.js 18+ (built-in fetch + crypto)
import crypto from 'node:crypto';

const API_BASE = 'https://gateway.vigasoft.lu';
// Append ?key=value&... here if your endpoint takes query params
const PATH_AND_QUERY = '/bizzlink/api/v1/ping';
const API_TOKEN = process.env.BIZZLINK_API_TOKEN;
const HMAC_SECRET = process.env.BIZZLINK_HMAC_SECRET;

const timestamp = Math.floor(Date.now() / 1000);
// GET has no body → hash the empty string
const bodyHash = crypto.createHash('sha256').update('').digest('hex');
const payload = `${timestamp}.GET.${PATH_AND_QUERY}.${bodyHash}`;
const signature = crypto
  .createHmac('sha256', HMAC_SECRET)
  .update(payload)
  .digest('hex');

const response = await fetch(`${API_BASE}${PATH_AND_QUERY}`, {
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'X-Bizzlink-Signature': `t=${timestamp},v1=${signature}`,
  },
});
console.log(await response.json());
<?php
// composer require guzzlehttp/guzzle
use GuzzleHttp\Client;

$apiBase    = 'https://gateway.vigasoft.lu';
// Append ?key=value&... here if your endpoint takes query params
$pathAndQuery = '/bizzlink/api/v1/ping';
$apiToken   = getenv('BIZZLINK_API_TOKEN');
$hmacSecret = getenv('BIZZLINK_HMAC_SECRET');

$timestamp = time();
// GET has no body → hash the empty string
$bodyHash  = hash('sha256', '');
$payload   = "{$timestamp}.GET.{$pathAndQuery}.{$bodyHash}";
$signature = hash_hmac('sha256', $payload, $hmacSecret);

$client = new Client();
$response = $client->get($apiBase . $pathAndQuery, [
    'headers' => [
        'Authorization'        => "Bearer {$apiToken}",
        'X-Bizzlink-Signature' => "t={$timestamp},v1={$signature}",
    ],
]);
echo $response->getBody();

You should get back a 200 OK with your tenant information.

Step 2: Send an Invoice

Use the Create & Send Invoice endpoint:

: "${BIZZLINK_API_TOKEN:?BIZZLINK_API_TOKEN not set}"
: "${BIZZLINK_HMAC_SECRET:?BIZZLINK_HMAC_SECRET not set}"

TIMESTAMP=$(date +%s)
PATH_AND_QUERY="/bizzlink/api/v1/document/create-and-send/invoice"
BODY='{
  "invoiceNumber": "INV-2026-001",
  "issueDate": "2026-02-21",
  "dueDate": "2026-03-21",
  "currencyCode": "EUR",
  "buyerReference": "PO-12345",
  "sender": {
    "name": "Your Company S.A.",
    "peppol": { "schemeId": "9938", "participantId": "LU12345678" },
    "vatNumber": "LU12345678",
    "address": {
      "street": "1 Rue du Commerce", "city": "Luxembourg",
      "postalCode": "1212", "country": "LU"
    },
    "email": "billing@yourcompany.lu",
    "bankAccount": { "iban": "LU000000000000000000", "bic": "BCEELULL" }
  },
  "receiver": {
    "name": "Acme Corp",
    "peppol": { "schemeId": "9930", "participantId": "DE123456789" },
    "vatNumber": "DE123456789",
    "billingAddress": {
      "street": "12 Hauptstrasse", "city": "Berlin",
      "postalCode": "10115", "country": "DE"
    }
  },
  "items": [
    { "name": "Consulting services", "unit": "HUR",
      "quantity": 10, "price": 150.00, "vatPercent": 17 }
  ]
}'

BODY_HASH=$(printf '%s' "${BODY}" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf '%s' "${TIMESTAMP}.POST.${PATH_AND_QUERY}.${BODY_HASH}" | \
  openssl dgst -sha256 -hmac "${BIZZLINK_HMAC_SECRET}" | sed 's/^.* //')

curl -X POST "https://gateway.vigasoft.lu${PATH_AND_QUERY}" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${BIZZLINK_API_TOKEN}" \
  -H "X-Bizzlink-Signature: t=${TIMESTAMP},v1=${SIGNATURE}" \
  -d "${BODY}"
// Maven: lu.vigasoft:bizzlink-sdk-java:0.3.1
// SDK signs, hashes the body and serializes JSON automatically.
import lu.vigasoft.bizzlink.sdk.BizzlinkClient;
import lu.vigasoft.bizzlink.sdk.requests.CreateAndSendInvoiceRequest;
import lu.vigasoft.bizzlink.api.DocumentApi;
import lu.vigasoft.bizzlink.model.*;
import java.time.LocalDate;
import java.math.BigDecimal;

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")
            .issueDate(LocalDate.parse("2026-02-21"))
            .dueDate(LocalDate.parse("2026-03-21"))
            .currencyCode(CurrencyCode.EUR)
            .buyerReference("PO-12345")
            .sender(new Party()
                .name("Your Company S.A.")
                .peppol(new PeppolId().schemeId("9938").participantId("LU12345678"))
                .vatNumber("LU12345678")
                .address(new Address()
                    .street("1 Rue du Commerce").city("Luxembourg")
                    .postalCode("1212").country("LU"))
                .email("billing@yourcompany.lu")
                .bankAccount(new BankAccount()
                    .iban("LU000000000000000000").bic("BCEELULL")))
            .receiver(new Party()
                .name("Acme Corp")
                .peppol(new PeppolId().schemeId("9930").participantId("DE123456789"))
                .vatNumber("DE123456789")
                .billingAddress(new Address()
                    .street("12 Hauptstrasse").city("Berlin")
                    .postalCode("10115").country("DE")))
            .addItemsItem(new InvoiceItem()
                .name("Consulting services").unit("HUR")
                .quantity(new BigDecimal("10")).price(new BigDecimal("150.00"))
                .vatPercent(new BigDecimal("17")))));

    System.out.println("Document ID: " + response.getData().getId());
}
import hashlib
import hmac
import json
import os
import time
import requests

API_BASE = "https://gateway.vigasoft.lu"
PATH_AND_QUERY = "/bizzlink/api/v1/document/create-and-send/invoice"
API_TOKEN = os.environ["BIZZLINK_API_TOKEN"]
HMAC_SECRET = os.environ["BIZZLINK_HMAC_SECRET"]

invoice = {
    "invoiceNumber": "INV-2026-001",
    "issueDate": "2026-02-21",
    "dueDate": "2026-03-21",
    "currencyCode": "EUR",
    "buyerReference": "PO-12345",
    "sender": {
        "name": "Your Company S.A.",
        "peppol": {"schemeId": "9938", "participantId": "LU12345678"},
        "vatNumber": "LU12345678",
        "address": {
            "street": "1 Rue du Commerce", "city": "Luxembourg",
            "postalCode": "1212", "country": "LU",
        },
        "email": "billing@yourcompany.lu",
        "bankAccount": {"iban": "LU000000000000000000", "bic": "BCEELULL"},
    },
    "receiver": {
        "name": "Acme Corp",
        "peppol": {"schemeId": "9930", "participantId": "DE123456789"},
        "vatNumber": "DE123456789",
        "billingAddress": {
            "street": "12 Hauptstrasse", "city": "Berlin",
            "postalCode": "10115", "country": "DE",
        },
    },
    "items": [
        {"name": "Consulting services", "unit": "HUR",
         "quantity": 10, "price": 150.00, "vatPercent": 17},
    ],
}

# IMPORTANT: serialize once, then both hash and send the exact same bytes
body = json.dumps(invoice, separators=(",", ":")).encode()

timestamp = int(time.time())
body_hash = hashlib.sha256(body).hexdigest()
payload = f"{timestamp}.POST.{PATH_AND_QUERY}.{body_hash}".encode()
signature = hmac.new(HMAC_SECRET.encode(), payload, hashlib.sha256).hexdigest()

response = requests.post(
    f"{API_BASE}{PATH_AND_QUERY}",
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_TOKEN}",
        "X-Bizzlink-Signature": f"t={timestamp},v1={signature}",
    },
    data=body,
)
print(response.json())
// Node.js 18+ (built-in fetch + crypto)
import crypto from 'node:crypto';

const API_BASE = 'https://gateway.vigasoft.lu';
const PATH_AND_QUERY = '/bizzlink/api/v1/document/create-and-send/invoice';
const API_TOKEN = process.env.BIZZLINK_API_TOKEN;
const HMAC_SECRET = process.env.BIZZLINK_HMAC_SECRET;

const invoice = {
  invoiceNumber: 'INV-2026-001',
  issueDate: '2026-02-21',
  dueDate: '2026-03-21',
  currencyCode: 'EUR',
  buyerReference: 'PO-12345',
  sender: {
    name: 'Your Company S.A.',
    peppol: { schemeId: '9938', participantId: 'LU12345678' },
    vatNumber: 'LU12345678',
    address: {
      street: '1 Rue du Commerce', city: 'Luxembourg',
      postalCode: '1212', country: 'LU',
    },
    email: 'billing@yourcompany.lu',
    bankAccount: { iban: 'LU000000000000000000', bic: 'BCEELULL' },
  },
  receiver: {
    name: 'Acme Corp',
    peppol: { schemeId: '9930', participantId: 'DE123456789' },
    vatNumber: 'DE123456789',
    billingAddress: {
      street: '12 Hauptstrasse', city: 'Berlin',
      postalCode: '10115', country: 'DE',
    },
  },
  items: [
    { name: 'Consulting services', unit: 'HUR',
      quantity: 10, price: 150.00, vatPercent: 17 },
  ],
};

// IMPORTANT: serialize once, then both hash and send the exact same bytes
const body = JSON.stringify(invoice);

const timestamp = Math.floor(Date.now() / 1000);
const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const payload = `${timestamp}.POST.${PATH_AND_QUERY}.${bodyHash}`;
const signature = crypto.createHmac('sha256', HMAC_SECRET)
  .update(payload).digest('hex');

const response = await fetch(`${API_BASE}${PATH_AND_QUERY}`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${API_TOKEN}`,
    'X-Bizzlink-Signature': `t=${timestamp},v1=${signature}`,
  },
  body,
});
console.log(await response.json());
<?php
// composer require guzzlehttp/guzzle
use GuzzleHttp\Client;

$apiBase    = 'https://gateway.vigasoft.lu';
$pathAndQuery = '/bizzlink/api/v1/document/create-and-send/invoice';
$apiToken   = getenv('BIZZLINK_API_TOKEN');
$hmacSecret = getenv('BIZZLINK_HMAC_SECRET');

$invoice = [
    'invoiceNumber' => 'INV-2026-001',
    'issueDate'     => '2026-02-21',
    'dueDate'       => '2026-03-21',
    'currencyCode'  => 'EUR',
    'buyerReference'=> 'PO-12345',
    'sender' => [
        'name'      => 'Your Company S.A.',
        'peppol'    => ['schemeId' => '9938', 'participantId' => 'LU12345678'],
        'vatNumber' => 'LU12345678',
        'address'   => [
            'street' => '1 Rue du Commerce', 'city' => 'Luxembourg',
            'postalCode' => '1212', 'country' => 'LU',
        ],
        'email'       => 'billing@yourcompany.lu',
        'bankAccount' => ['iban' => 'LU000000000000000000', 'bic' => 'BCEELULL'],
    ],
    'receiver' => [
        'name'      => 'Acme Corp',
        'peppol'    => ['schemeId' => '9930', 'participantId' => 'DE123456789'],
        'vatNumber' => 'DE123456789',
        'billingAddress' => [
            'street' => '12 Hauptstrasse', 'city' => 'Berlin',
            'postalCode' => '10115', 'country' => 'DE',
        ],
    ],
    'items' => [
        ['name' => 'Consulting services', 'unit' => 'HUR',
         'quantity' => 10, 'price' => 150.00, 'vatPercent' => 17],
    ],
];

// IMPORTANT: serialize once, then both hash and send the exact same bytes
$body = json_encode($invoice, JSON_UNESCAPED_SLASHES);

$timestamp = time();
$bodyHash  = hash('sha256', $body);
$payload   = "{$timestamp}.POST.{$pathAndQuery}.{$bodyHash}";
$signature = hash_hmac('sha256', $payload, $hmacSecret);

$client = new Client();
$response = $client->post($apiBase . $pathAndQuery, [
    'headers' => [
        'Content-Type'         => 'application/json',
        'Authorization'        => "Bearer {$apiToken}",
        'X-Bizzlink-Signature' => "t={$timestamp},v1={$signature}",
    ],
    'body' => $body,
]);
echo $response->getBody();

Step 3: Check the Response

A successful response is a JSON:API collectiondata is an array of generated renditions. For a typed-invoice call from a LU/AT/BE/NL/… seller this is a single-element array (UBL via Peppol). For an IT seller, the same input would produce two entries (FatturaPA via SDI and UBL via Peppol), sharing a processId.

{
  "data": [
    {
      "type": "invoices",
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "attributes": {
        "processId": "8e3a1f2c-1234-4abc-9def-012345678abc",
        "format": "UBL_PEPPOL_BIS_3",
        "target": "PEPPOL",
        "status": "QUEUED_FOR_VALIDATION",
        "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Invoice>...</Invoice>"
      }
    }
  ]
}
FieldDescription
data[].typeJSON:API resource type — invoices or credit-notes (per rendition).
data[].idUnique document id for this rendition. Use it to poll GET /documents/{id}/status.
attributes.processIdShared across all renditions from one create call. Use GET /documents?processId={uuid} to list siblings.
attributes.formatWire format of the generated XML (UBL_PEPPOL_BIS_3, FATTURA_PA_1_2, FACTUR_X_1_07, XRECHNUNG_3_0, KSEF_FA_3).
attributes.targetDestination network (PEPPOL, SDI_IT, PPF_FR, XRECHNUNG_DE, KSEF_PL).
attributes.statusSymbolic status — typically QUEUED_FOR_VALIDATION (async validation pending), VALID, or FAILED.
attributes.xmlThe generated XML in the indicated format.
Your invoice has been generated, validated, and queued for delivery via the Peppol network.

What’s Next?