Skip to main content

FactPulse REST API

Project description

FactPulse SDK Python

Official Python client for the FactPulse API - French electronic invoicing.

Features

  • Factur-X: Generation and validation of electronic invoices (MINIMUM, BASIC, EN16931, EXTENDED profiles)
  • Chorus Pro: Integration with the French public invoicing platform
  • AFNOR PDP/PA: Submission of flows compliant with XP Z12-013 standard
  • Electronic signature: PDF signing (PAdES-B-B, PAdES-B-T, PAdES-B-LT)
  • Thin HTTP wrapper: Generic post() and get() methods with automatic JWT auth and polling

Installation

pip install factpulse

Quick Start

import base64
from factpulse_helpers import FactPulseClient

# Create the client
client = FactPulseClient(
    email="your_email@example.com",
    password="your_password",
    client_uid="your-client-uuid",  # From dashboard: Configuration > Clients
)

# Read your source PDF
with open("source_invoice.pdf", "rb") as f:
    pdf_b64 = base64.b64encode(f.read()).decode()

# Generate Factur-X and submit to PDP in one call
result = client.post(
    "processing/invoices/submit-complete-async",
    invoiceData={
        "number": "INV-2025-001",
        "supplier": {
            "name": "ACME Corporation",
            "siret": "12345678901234",
            "iban": "FR7630001007941234567890185",
            "routing_address": "12345678901234",
        },
        "recipient": {
            "name": "Client Company SA",
            "siret": "98765432109876",
            "routing_address": "98765432109876",
        },
        "lines": [
            {
                "description": "Consulting services",
                "quantity": 10,
                "unitPrice": 100.0,
                "vatRate": 20.0,
            }
        ],
    },
    sourcePdf=pdf_b64,
    profile="EN16931",
    destination={"type": "afnor"},
)

# PDF is in result["content"] (auto-polled, auto-decoded from base64)
with open("facturx_invoice.pdf", "wb") as f:
    f.write(result["content"])

print(f"Flow ID: {result['afnorResult']['flowId']}")

API Methods

The SDK provides two generic methods that map directly to API endpoints:

# POST /api/v1/{path}
result = client.post("path/to/endpoint", key1=value1, key2=value2)

# GET /api/v1/{path}
result = client.get("path/to/endpoint", param1=value1)

Common Endpoints

Endpoint Method Description
processing/invoices/submit-complete-async POST Generate Factur-X + submit to PDP
processing/generate-invoice POST Generate Factur-X XML or PDF
processing/validate-xml POST Validate Factur-X XML
processing/validate-facturx-pdf POST Validate Factur-X PDF
processing/sign-pdf POST Sign PDF with certificate
afnor/flow/v1/flows POST Submit flow to AFNOR PDP
afnor/incoming-flows/{flow_id} GET Get incoming invoice
chorus-pro/factures/soumettre POST Submit to Chorus Pro

Webhooks

Instead of polling, you can receive results via webhook by adding callbackUrl:

# Submit with webhook - returns immediately
result = client.post(
    "processing/invoices/submit-complete-async",
    invoiceData=invoice_data,
    sourcePdf=pdf_b64,
    destination={"type": "afnor"},
    callbackUrl="https://your-server.com/webhook/factpulse",
    webhookMode="INLINE",  # or "DOWNLOAD_URL"
)

task_id = result["taskId"]
# Result will be POSTed to your webhook URL

Webhook Receiver Example (Flask)

import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = "your-shared-secret"

def verify_signature(payload: bytes, signature: str) -> bool:
    if not signature.startswith("sha256="):
        return False
    expected = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature[7:], expected)

@app.route("/webhook/factpulse", methods=["POST"])
def webhook_handler():
    signature = request.headers.get("X-Webhook-Signature", "")
    if not verify_signature(request.data, signature):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.json
    event_type = event["event_type"]
    data = event["data"]

    if event_type == "submission.completed":
        flow_id = data.get("afnorResult", {}).get("flowId")
        print(f"Invoice submitted: {flow_id}")
    elif event_type == "submission.failed":
        print(f"Submission failed: {data.get('error')}")

    return jsonify({"status": "received"})

Webhook Event Types

Event Description
generation.completed Factur-X generated successfully
generation.failed Generation failed
validation.completed Validation passed
validation.failed Validation failed
signature.completed PDF signed
submission.completed Submitted to PDP/Chorus
submission.failed Submission failed

Zero-Storage Mode

Pass PDP credentials directly in the request (no server-side storage):

result = client.post(
    "processing/invoices/submit-complete-async",
    invoiceData=invoice_data,
    sourcePdf=pdf_b64,
    destination={
        "type": "afnor",
        "flowServiceUrl": "https://api.pdp.example.com/flow/v1",
        "tokenUrl": "https://auth.pdp.example.com/oauth/token",
        "clientId": "your_pdp_client_id",
        "clientSecret": "your_pdp_client_secret",
    },
)

Error Handling

from factpulse_helpers import FactPulseClient, FactPulseError

try:
    result = client.post("processing/validate-xml", xmlContent=xml_string)
except FactPulseError as e:
    print(f"Error: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Details: {e.details}")  # Validation errors list

Available Helpers

The SDK provides the following helper classes:

  • FactPulseClient: Main HTTP client with auto-auth and polling
  • FactPulseError: Base exception class
  • FactPulseAuthError: Authentication failure
  • FactPulseValidationError: Validation errors with details
  • FactPulsePollingTimeout: Task polling timeout

Resources

License

MIT License - Copyright (c) 2025 FactPulse

Project details


Release history Release notifications | RSS feed

This version

4.3.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

factpulse-4.3.0.tar.gz (371.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

factpulse-4.3.0-py3-none-any.whl (1.4 MB view details)

Uploaded Python 3

File details

Details for the file factpulse-4.3.0.tar.gz.

File metadata

  • Download URL: factpulse-4.3.0.tar.gz
  • Upload date:
  • Size: 371.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for factpulse-4.3.0.tar.gz
Algorithm Hash digest
SHA256 a938094b57aaffae826a4d1098ca476afbdf18d457f975cc2357cf4cfbd6d019
MD5 101cf1bf6c227a144ac673a89a63124e
BLAKE2b-256 3a3bc8ac0689494d80684ab40290fcf3d300f1a8fa645735a97ab90693ce9ceb

See more details on using hashes here.

File details

Details for the file factpulse-4.3.0-py3-none-any.whl.

File metadata

  • Download URL: factpulse-4.3.0-py3-none-any.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for factpulse-4.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 258a8506449171987a861aaa09382b9c5428cd1e5e8d86d6b317a8f239a36b50
MD5 25085c76d89b02e94fc36d6d57ca3826
BLAKE2b-256 5d2b01f5c3baf4b092771f6bdffa80030f7b342705a9cd0eae687747907ee845

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page