POST /v1/validate — examples by language

Request: the raw XML/PDF document as body. Response: JSON (compliance report).

curl

curl -X POST "https://api.example/v1/validate" \
  -H "Authorization: Bearer $FACTURX_API_KEY" \
  --data-binary @document.xml

PHP

<?php
$ch = curl_init('https://api.example/v1/validate');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('FACTURX_API_KEY')],
    CURLOPT_POSTFIELDS => file_get_contents('document.xml'),
    CURLOPT_RETURNTRANSFER => true,
]);
$report = json_decode(curl_exec($ch), true);
if (!$report['conforme']) {
    foreach ($report['violations'] as $v) echo $v['message'] . "\n";
}

Python

import requests, os

with open("document.xml", "rb") as f:
    resp = requests.post(
        "https://api.example/v1/validate",
        headers={"Authorization": f"Bearer {os.environ['FACTURX_API_KEY']}"},
        data=f,
    )
report = resp.json()
print("conforme:", report["conforme"], "— rule pack:", report["rulepack"]["id"])

JavaScript (Node.js)

import { readFile } from "node:fs/promises";

const doc = await readFile("document.xml");
const resp = await fetch("https://api.example/v1/validate", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.FACTURX_API_KEY}` },
  body: doc,
});
const report = await resp.json();
console.log("conforme:", report.conforme);

Go

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	f, _ := os.Open("document.xml")
	defer f.Close()

	req, _ := http.NewRequest(http.MethodPost, "https://api.example/v1/validate", f)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("FACTURX_API_KEY"))

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	var report struct {
		Conforme bool `json:"conforme"`
		Rulepack struct{ ID string `json:"id"` } `json:"rulepack"`
	}
	json.NewDecoder(resp.Body).Decode(&report)
	fmt.Println("conforme:", report.Conforme, "rule pack:", report.Rulepack.ID)
}

What schematronChecked means

Since Story 3.7, ?profile=EN16931 and ?profile=EXTENDED-CTC-FR (the default if omitted) actually run the official FNFE-MPE schematron (schematronChecked: true) — the report checks XSD and the BR-* business rules (reference repository fnfempe/France_RFE, release v1.4.0.02), with the official BR-* code, the XPath and the message in each violation.

Since Story 13.1, ?profile=PEPPOL-BIS likewise runs the official OpenPEPPOL schematron (rule pack ubl-peppol-bis-3.0.20) — rule codes BR-* (CEN/EN16931 core) or PEPPOL-EN16931-R* (rules specific to the Peppol network). This profile is also selected automatically on a UBL document whose cbc:CustomizationID declares targeting Peppol BIS Billing 3.0, with no explicit parameter — see Peppol/Belgium regulation.

⚠️ Limits that remain, honestly documented:

  • No automated non-regression corpus in CI (only a few official examples have been spot-checked — see rulepacks/README.md).
  • conforme: true remains relative to the rule pack applied, never an absolute guarantee of regulatory compliance (the reference repository itself can evolve, or a case not covered by the tested examples may escape this check) — see the regulation page.