Import a Supplier Invoice via API

Use this guide to import supplier (purchase) invoices into Pennylane using the API — for example, from your ERP, OCR, or document capture tool.

Goal

This tutorial explains how to automatically import supplier invoices so they appear in your workspace, linked to the right supplier and ready for accounting and reconciliation.

You will learn how to:

  1. Upload a supplier invoice file (PDF)
  2. Import the invoice via the API
  3. (Optional) Categorize it for reporting

End Result

Your supplier invoice appears in Pennylane, linked to the right supplier and ledger accounts, fully integrated into your accounting workflow.

👥

Who is this for?

Developers and partners building integrations that sync supplier invoices from external systems into Pennylane.

🔒

Authentication

Partner integrations must use OAuth 2.0.

In sandbox, you can test with a Company API token, but OAuth 2.0 is required for Marketplace production integrations.

Before You Get Started

Required scopes (Company API v2)

ScopeFeatures
supplier_invoices:allCreate or import supplier invoices
file_attachments:allUpload and attach supplier invoice PDFs
(optional) ledger_accounts:readonlyRetrieve account IDs for accurate accounting mapping
(optional) categories:allCategorize invoices for reporting

You will need:

  • A Pennylane company selected through OAuth consent
  • Your OAuth access token (or a Company token in sandbox)
  • A valid supplier_id (existing or newly created)
  • A valid invoice file (PDF) to attach
  • (Optional) ledger account IDs — e.g., 60x for purchases/expenses, 4456 for deductible VAT, 401 for suppliers
ℹ️

IDs are company-specific

Supplier and ledger account IDs differ per company. Store them per tenant to avoid mismatches.

📘

See also:

Step 1 | (Optional) Verifying Authentication

Before importing invoices, confirm that your token and environment are correctly configured.

curl https://app.pennylane.com/api/external/v2/me \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Expected response:

200 OK - authentication successful.

💡

Tip: Run this check when setting up your integration for the first time, or when switching between **sandbox **and production.

Step 2 | Uploading the Invoice File

Before importing the supplier invoice, upload the PDF file to Pennylane.

curl --request POST \
  --url https://app.pennylane.com/api/external/v2/file_attachments \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: multipart/form-data" \
  -F [email protected]

Result

The API returns a JSON object containing an id — use this file_attachment_id in the next step.

Example response:

{
  "id": 13245,
  "filename": "invoice-october.pdf",
  "status": "uploaded"
}
💡

Accepted file format

Only PDF files are supported for supplier invoice imports.

The /file_attachments endpoint accepts files up to 100 MB.

Uploading another format for an invoice will return a validation error (typically 400 Bad Request).

Step 3 | Importing the Supplier Invoice

Now that your invoice file is uploaded, use the import endpoint to create the corresponding invoice in Pennylane.

curl --request POST \
  --url https://app.pennylane.com/api/external/v2/supplier_invoices/import \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "file_attachment_id": 13245,
    "supplier_id": 98123,
    "date": "2025-10-01",
    "deadline": "2025-10-31",
    "currency_amount_before_tax": "100.00",
    "currency_tax": "20.00",
    "currency_amount": "120.00",
    "invoice_lines": [
      {
        "ledger_account_id": 601002,
        "currency_amount": "120.00",
        "currency_tax": "20.00",
        "vat_rate": "FR_200"
      }
    ]
  }'

Result

The invoice is created and appears in your Pennylane workspace, linked to the specified supplier.

💡

Tip: Amount field types

All numeric amounts (e.g. currency_amount, currency_tax, currency_amount_before_tax) must be provided as strings, not numbers.

Example response:

{
  "id": 4431,
  "status": "imported",
  "supplier_name": "ACME Office Supplies",
  "currency_amount": "120.00"
}
⚠️

Important

The sum of all invoice_lines.currency_amount values must equal the total currency_amount.

Otherwise, the API returns: 422 Unprocessable Entity — Entry lines are not balanced.

💡

Ledger Account Mapping

If you omit the ledger_account_id, Pennylane automatically applies the company’s default mapping (chart of accounts & VAT settings).

You can override this by specifying a ledger_account_id explicitly.

📘

VAT Rate Codes Reference

vat_rate must use a supported code.

CodeDescriptionRate
FR_200Standard VAT France20%
FR_100Reduced VAT France10%
FR_055Reduced VAT France5.5%
exemptExempt (0%)0%
anyNo specific VAT code

For the full list of supported VAT codes, see the API Reference for Import Supplier Invoice.

Step 4 | Validating the Import

You can verify that the invoice was successfully created:

curl --request GET \
  --url https://app.pennylane.com/api/external/v2/supplier_invoices/4431 \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Result

{
  "id": 4431,
  "status": "imported",
  "currency_amount": "120.00",
  "supplier_name": "ACME Office Supplies"
}
⚠️

Common errors

  • 401 Unauthorized → Invalid or expired token
  • 403 Forbidden → Missing scope (supplier_invoices:all)
  • 422 Unprocessable Entity → VAT or totals mismatch
💡

Tip: In sandbox, you can safely test imports with fake suppliers and sample amounts.

Step 5 | (Optional) Categorizing the Invoice

Categorization helps analyze expenses by department, cost center, or activity.

1. Get or Create a Category

# List categories
GET /api/external/v2/categories

# Create a new category
POST /api/external/v2/categories

2. Assign the Category to the Invoice

curl --request PUT \
  --url https://app.pennylane.com/api/external/v2/supplier_invoices/{invoice_id}/categories \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '[
    { "id": 123, "weight": 1.0 }
  ]'
📘

Note

weight represents the allocation percentage (1.0 = 100%, 0.5 = 50%). Multiple categories are supported.

Test in Sandbox or Postman

You can test this workflow safely using your Pennylane Sandbox or the official Postman collection.

  • Collection: Pennylane API v2 → Supplier Invoices → Import
  • Replace <ACCESS_TOKEN> with your sandbox token
  • Use real supplier_id and ledger_account_id from your sandbox company

Result

Testing in sandbox ensures your flow works end-to-end before deploying to production.

Common Pitfalls

IssueLikely CauseHow to Fix
400 Bad RequestInvalid payload: unexpected field or missing required propertyCheck field names, required fields, and data types
401 UnauthorizedMissing or expired tokenRefresh token or fix header
403 ForbiddenMissing scopeAdd supplier_invoices:all
404 File not foundInvalid/expired file_attachment_idRe-upload via /file_attachments
422 Entry lines are not balancedHT/TVA/TTC mismatchRecalculate totals

Best Practices

  • Validate before import — totals & VAT consistency
  • Use appropriate accounts — e.g., 60x expenses, 4456 deductible VAT, 401 suppliers
  • Secure tokens — rotate & store safely
  • Test in sandbox first — then roll out to production
💡

Duplicate handling

Pennylane automatically prevents duplicates by rejecting supplier invoices whose attached PDF (file_attachment_id) already exists in the workspace — whether it was imported manually or via API.

If you try to re-import the same file, the API returns 422 Unprocessable Entity.

How Pennylane Handles Accounting

When you import a supplier invoice:

  • Pennylane automatically creates expense entries in your accounting ledgers.
  • The entries appear in your Purchases journal.
  • The invoice is visible in Invoicing → Supplier Invoices in your workspace.
📘

Note

Entries are generated according to your company’s chart of accounts and the ledger_account_id values provided in your payload.