Skip to main content

Ledgentic Partner Integration Guide

Integrate your application with Ledgentic to automate supplier-invoice ingestion, bookkeeping analysis, and status synchronization across your own systems.

Introduction

The Partner Integration API enables you to upload supplier invoices, receive status updates through signed webhooks, and fetch granular bookkeeping results once processing is finished. This guide walks you through the concepts, workflow, and endpoints required for a full integration scenario.

Prerequisites

Make sure you have the following prerequisites before you start:

  • Organizations – You need to have at least one organization in the admin interface.
  • API Key – Generated an API key in the admin interface.
  • Organization External ID Mappings: Make sure you add the external mapping ID to each Organization in the amdin interface that you want to use the API for.
  • Webhooks: Set a webhook url and save the webhook sercret securely. Child organizations can inherit a parent webhook which is the recommended approach.
  • API Key Access Scopes: Make sure the api key has the correct access scopes to the parent and child orginaizations that will use the API.

Key Concepts

  • Organizations & delegated access – An API key is scoped to a root organization and can optionally include access to selected child organizations. Configure child access for each key inside the Ledgentic admin app before calling the API.
  • External organization ID (externalOrgId) – Provision the identifier in the Ledgentic admin interface during onboarding before you attempt any API calls. Provide it as the path parameter on every request so traffic is routed to the correct organization.
  • Invoice external ID (externalId) – The unique identifier for each supplier invoice in your system. Supply it in the payload when creating an invoice and reuse the same value wherever the API path references {externalInvoiceId}.
  • Signed webhooks – When processing completes, Ledgentic calls the webhook configured for the organization in the admin interface (child organizations can inherit a parent webhook). Each payload is signed with the webhook secret and delivered in the X-Webhook-Signature header (t={timestamp},v1={signature}); verify it before acting on the request.
  • Processing lifecycle – Invoices transition through data extraction, accounting analysis, optional review, and completion. Use the provided endpoints to monitor progress and consume the bookkeeping output.

Authentication & Base URL

All endpoints accept API key authentication only. Include your key on every request via the X-API-Key header.

Base URL: https://api.ledgentic.com
Header: X-API-Key: YOUR_API_KEY

Rate Limiting

Partner endpoints enforce per-API-key rate limits. When a limit is exceeded the API returns HTTP 429 along with a Retry-After header.

  • Default production quota (subject to change): 500 requests per minute per API key. Contact us if you need a higher quota.
  • Upload, read, and change-request endpoints share the same quota; distribute calls accordingly.
  • Implement client-side backoff when you receive HTTP 429 responses and surface metrics so you can request higher limits if needed.

Integration Workflow

  1. Submit invoice for processing – Upload the supplier invoice document for a given externalOrgId.
  2. Handle the completion webhook – Ledgentic notifies your webhook endpoint when processing finishes (successfully or with errors).
  3. Retrieve bookkeeping data – Fetch the processed invoice and journal entry data using the original externalId.

Repeat the workflow for each invoice you need to process. You can also list invoices, submit bookkeeping change requests, and delete drafts when required.

Submit a Supplier Invoice

Endpoint

POST /partner/v2/organizations/{externalOrgId}/supplier-invoices

Ensure the organization is onboarded and the externalOrgId is registered in the admin interface before making this request.

curl -X POST \
"https://api.ledgentic.com/partner/v2/organizations/acme-001/supplier-invoices" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "inv-87342",
"fileName": "supplier_invoice_87342.pdf",
"base64File": "JVBERi0xLjQKJcfs..."
}'
FieldTypeRequiredDescription
externalIdstringYesYour unique invoice identifier (externalId).
fileNamestringYesName of the uploaded invoice file.
base64FilestringYesBase64-encoded binary contents (PDF, image).

Response 201

{
"id": "inv_abc123",
"externalOrgId": "acme-001",
"fileName": "supplier_invoice_87342.pdf",
"status": "NOT_STARTED",
"createdAt": "2024-01-15T10:30:00Z",
"message": "Invoice uploaded successfully for organization 'acme-001'",
"metadata": {
"source": "PARTNER_API",
"partnerOrgId": "partner-42",
"uploadedAt": "2024-01-15T10:30:00Z"
}
}

Note: Batch uploads are not yet supported. Submit one invoice per request and parallelize responsibly within your rate limits.

Processing Status Reference

Invoices move through the following status values:

StatusMeaning
NOT_STARTEDInvoice accepted and queued.
SUPPLIER_EXTRACTION_PROCESSINGSupplier data extraction in progress.
SUPPLIER_EXTRACTION_ERRORSupplier extraction failed.
OCR_PROCESSINGOCR is running.
OCR_DONE / OCR_ERROROCR finished successfully or failed.
PROCESSING / PROCESSING_ERRORBookkeeping analysis running or failed.
ACCOUNTING_PROCESSINGJournal entry construction in progress.
ACCOUNTING_DONE / ACCOUNTING_ERRORAccounting step finished or failed.
WAITING_FOR_HUMANManual review required.
SUCCESSProcessing complete; data available for retrieval.
ERRORProcessing failed; investigate error details.

Configure and Verify Webhooks

Create webhook endpoints in the Ledgentic admin app. Each webhook has a dedicated secret that you must store securely.

  • Ledgentic delivers events with the externalOrgId and externalId so that you can correlate them in your system.
  • Every payload is signed with HMAC-SHA256 using the webhook secret and sent in the X-Webhook-Signature header (t={timestamp},v1={signature}).
  • Validate the signature and ensure the timestamp is within an acceptable tolerance (for example, five minutes) before acknowledging the request. Return HTTP 200 within 30 seconds.
  • Configuring a webhook on the parent organization is sufficient; child organizations that inherit the parent webhook will receive callbacks at the same endpoint.

Example webhook payload

{
"externalOrgId": "acme-001",
"externalId": "inv-87342",
"status": "SUCCESS",
"completedAt": "2024-01-15T10:45:00Z",
"bookkeepingEndpoint": "/partner/v2/organizations/acme-001/supplier-invoices/inv-87342/bookkeeping"
}

Webhook Signature Verification

Ledgentic computes the webhook signature as HMAC_SHA256( {timestamp}.{raw_request_body} ) with your webhook secret.

  1. Read the X-Webhook-Signature header and parse t={timestamp},v1={signature} pairs.
  2. Concatenate the timestamp and raw JSON body with a dot separator to form the signed message.
  3. Hash the message with HMAC-SHA256 using your webhook secret.
  4. Compare the computed digest with the provided signature using a constant-time equality check.
  5. Reject requests where the timestamp is older than your configured tolerance (typically ≤5 minutes) to prevent replay attacks.

Node.js example

import crypto from "node:crypto";

export function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const parts = signatureHeader.split(",");
const timestampPart = parts.find((part) => part.startsWith("t="));
const signaturePart = parts.find((part) => part.startsWith("v1="));

if (!timestampPart || !signaturePart) {
return { valid: false, reason: "Missing timestamp or signature" };
}

const timestamp = timestampPart.replace("t=", "");
const timestampNumber = Number(timestamp);
if (!Number.isFinite(timestampNumber)) {
return { valid: false, reason: "Invalid timestamp" };
}

const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestampNumber) > 5 * 60) {
return { valid: false, reason: "Timestamp outside tolerance" };
}

const providedSignature = signaturePart.replace("v1=", "");
const message = `${timestamp}.${rawBody}`;

const expectedSignature = crypto
.createHmac("sha256", secret)
.update(message)
.digest("hex");

const providedBuffer = Buffer.from(providedSignature, "hex");
const expectedBuffer = Buffer.from(expectedSignature, "hex");

const signaturesMatch =
providedBuffer.length === expectedBuffer.length &&
crypto.timingSafeEqual(providedBuffer, expectedBuffer);

return { valid: signaturesMatch, timestamp: timestampNumber };
}

Implement these checks in your webhook handler using your preferred language and ensure stale or invalid signatures are rejected before business logic runs.

If the webhook indicates failure (status ending with _ERROR), fetch the bookkeeping resource to inspect the error details and decide whether to resubmit or create a change request.

Retrieve Processed Invoice & Bookkeeping Data

Endpoint

GET /partner/v2/organizations/{externalOrgId}/supplier-invoices/{externalInvoiceId}/bookkeeping

Use your invoice externalId in place of {externalInvoiceId} when building the request URL.

curl \
"https://api.ledgentic.com/partner/v2/organizations/acme-001/supplier-invoices/inv-87342/bookkeeping" \
-H "X-API-Key: YOUR_API_KEY"

The response bundles supplier invoice metadata, supplier master data, and the generated journal entry. Fields such as line items, VAT breakdowns, and journal lines are included only when available.

List Supplier Invoices

Endpoint

GET /partner/v2/organizations/{externalOrgId}/supplier-invoices

Use this endpoint to page through all invoices for an organization or filter by status.

QueryTypeDescription
pagenumber1-based page index (default: 1).
limitnumberItems per page (default: 20, max: 100).
statusstringFilter by processing status (see table above).
showAllbooleanSet to true to include invoices without an external ID (default: false).

The response returns an items array and meta pagination object so you can iterate through result pages.

Submit Bookkeeping Change Requests

When you need to adjust extracted data or journal entries, submit a change request.

Endpoint

PATCH /partner/v2/organizations/{externalOrgId}/supplier-invoices/{externalInvoiceId}/bookkeeping

Use the same externalId value you supplied during upload in the {externalInvoiceId} placeholder.

curl -X PATCH \
"https://api.ledgentic.com/partner/v2/organizations/acme-001/supplier-invoices/inv-87342/bookkeeping" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"supplierInvoice": {
"invoiceNumber": "87342",
"dueDate": "2025-05-28",
"currency": "SEK"
},
"bookkeeping": {
"lines": [
{
"accountNumber": "2440",
"credit": "12500.00",
"description": "Accounts payable"
}
]
}
}'

The API responds with a CreateChangeRequestResponseDto containing the change request ID and current status. You can poll for updates using:

  • GET /partner/v2/organizations/{externalOrgId}/supplier-invoices/{externalInvoiceId}/change-requests
  • GET /partner/v2/organizations/{externalOrgId}/supplier-invoices/{externalInvoiceId}/change-requests/{changeRequestId}

Use your invoice externalId for the {externalInvoiceId} placeholder when calling these endpoints.

Multiple change requests can coexist for the same invoice. When a new request is submitted, it joins the review queue independently—track each changeRequestId until it resolves to APPLIED, REJECTED, or EXPIRED.

Delete an Invoice

Delete invoices that are still in draft bookkeeping state with:

DELETE /partner/v2/organizations/{externalOrgId}/supplier-invoices/{externalInvoiceId}

Replace {externalInvoiceId} with your invoice externalId to target the correct record.

A successful deletion returns HTTP 200. Attempts to delete invoices with posted or reviewed journal entries result in HTTP 403, and unknown invoices return HTTP 404.

Error Handling & Retries

  • Rely on HTTP status codes for coarse error handling (400 invalid payload, 401/403 authentication issues, 404 resource missing, 429 rate limit, 500 unexpected error).
  • Responses include structured error payloads to help you recover quickly; log them for diagnostics.
  • Webhooks should be idempotent. If Ledgentic does not receive a 2xx response, it will retry delivery with exponential backoff. Always verify the signature on each retry.
  • HTTP 429 responses contain a Retry-After header (seconds) you can honor before resubmitting.