Pennylane E-Invoicing (Plateforme Agréée) API

Manage French electronic-invoicing onboarding via Plateforme Agréée (PA) registrations, and send customer e-invoices to the partner dematerialization platform.

OpenAPI Specification

pennylane-openapi.yml Raw ↑
# Provenance: modeled-from-docs (2026-07-17)
# Source: Pennylane Company API v2 developer documentation at https://pennylane.readme.io
#   - Endpoint list + operationIds: https://pennylane.readme.io/llms.txt and /reference/*
#   - Confirmed base URL from live reference example requests: https://app.pennylane.com/api/external/v2
#   - Auth (Bearer token: Company API token, Firm API token, or OAuth 2.0 access token)
#     confirmed from https://pennylane.readme.io/docs/getting-started and /docs/oauth-20-walkthrough
# No first-party OpenAPI/Swagger file is published for download; this document models the
# documented REST surface. Paths and operationIds mirror the published API Reference; a
# representative (not exhaustive) subset of Pennylane's ~150 v2 operations is included.
openapi: 3.0.1
info:
  title: Pennylane Company API
  description: >-
    Pennylane is a French financial and accounting operating system for SMEs and
    accounting firms. The Company API (v2) lets companies, firms, and integration
    partners access and sync invoicing, accounting, banking, and financial data,
    and automate end-to-end workflows. V2 is the stable version; V1 is deprecated.
  termsOfService: https://pennylane.readme.io/docs/api-contract-terms
  contact:
    name: Pennylane API Support
    url: https://pennylane.readme.io/docs/how-to-reach-out-to-us
  version: '2.0'
servers:
  - url: https://app.pennylane.com/api/external/v2
    description: Production (v2)
security:
  - bearerAuth: []
tags:
  - name: Account
  - name: Journals
  - name: Ledger Accounts
  - name: Ledger Entries
  - name: Accounting Exports
  - name: Categories
  - name: Customer Invoices
  - name: Supplier Invoices
  - name: Quotes
  - name: Billing Subscriptions
  - name: Products
  - name: Customers
  - name: Suppliers
  - name: Bank Accounts
  - name: Transactions
  - name: Mandates
  - name: File Attachments
  - name: Change Events
  - name: E-Invoicing
paths:
  /me:
    get:
      operationId: getMe
      tags: [Account]
      summary: User profile
      description: Returns information about the company and the user associated to the token.
      responses:
        '200':
          description: OK
  /journals:
    get:
      operationId: getJournals
      tags: [Journals]
      summary: List journals
      description: 'List journals. Requires scope: journals:readonly or journals:all.'
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { description: OK }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    post:
      operationId: postJournals
      tags: [Journals]
      summary: Create a journal
      description: 'Create a journal. Requires scope: journals:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /journals/{id}:
    get:
      operationId: getJournal
      tags: [Journals]
      summary: Retrieve a journal
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
        '404': { $ref: '#/components/responses/NotFound' }
  /ledger_accounts:
    get:
      operationId: getLedgerAccounts
      tags: [Ledger Accounts]
      summary: List ledger accounts
      description: 'Requires scope: ledger_accounts:readonly or ledger_accounts:all.'
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { description: OK }
    post:
      operationId: postLedgerAccounts
      tags: [Ledger Accounts]
      summary: Create a ledger account
      description: 'Requires scope: ledger_accounts:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /ledger_accounts/{id}:
    get:
      operationId: getLedgerAccount
      tags: [Ledger Accounts]
      summary: Get a ledger account
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: updateLedgerAccount
      tags: [Ledger Accounts]
      summary: Update a ledger account
      description: 'Requires scope: ledger_accounts:all.'
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /ledger_entries:
    get:
      operationId: getLedgerEntries
      tags: [Ledger Entries]
      summary: List ledger entries
      description: 'Requires scope: ledger_entries:readonly or ledger_entries:all.'
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { description: OK }
    post:
      operationId: postLedgerEntries
      tags: [Ledger Entries]
      summary: Create a ledger entry
      description: 'Requires scope: ledger_entries:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /ledger_entries/{id}:
    get:
      operationId: getLedgerEntry
      tags: [Ledger Entries]
      summary: Retrieve a ledger entry
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: putLedgerEntries
      tags: [Ledger Entries]
      summary: Update a ledger entry
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /trial_balance:
    get:
      operationId: getTrialBalance
      tags: [Ledger Entries]
      summary: Get the trial balance
      description: 'Returns the trial balance for the given period. Requires scope: trial_balance:readonly.'
      parameters:
        - name: period_start
          in: query
          schema: { type: string, format: date }
        - name: period_end
          in: query
          schema: { type: string, format: date }
      responses:
        '200': { description: OK }
  /fiscal_years:
    get:
      operationId: getFiscalYears
      tags: [Ledger Entries]
      summary: List company fiscal years
      description: 'Requires scope: fiscal_years:readonly.'
      responses:
        '200': { description: OK }
  /exports/general_ledger:
    post:
      operationId: exportGeneralLedger
      tags: [Accounting Exports]
      summary: Create a General Ledger export
      description: 'Creates an xlsx General Ledger export. Requires scope: exports:gl.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /exports/fec:
    post:
      operationId: exportFec
      tags: [Accounting Exports]
      summary: Create a FEC export
      description: 'Creates a FEC (Fichier des Ecritures Comptables) export. Requires scope: exports:fec.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /categories:
    get:
      operationId: getCategories
      tags: [Categories]
      summary: List categories
      description: 'Requires scope: categories:readonly or categories:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postCategories
      tags: [Categories]
      summary: Create a category
      description: 'Requires scope: categories:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /category_groups:
    get:
      operationId: getCategoryGroups
      tags: [Categories]
      summary: List category groups
      responses:
        '200': { description: OK }
  /customer_invoices:
    get:
      operationId: getCustomerInvoices
      tags: [Customer Invoices]
      summary: List customer invoices
      description: 'List customer invoices and credit notes. Requires scope: customer_invoices:readonly or customer_invoices:all.'
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Filter'
      responses:
        '200': { description: OK }
        '429': { $ref: '#/components/responses/TooManyRequests' }
    post:
      operationId: postCustomerInvoices
      tags: [Customer Invoices]
      summary: Create a customer invoice
      description: 'Create a draft or finalized customer invoice or credit note. Requires scope: customer_invoices:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /customer_invoices/import:
    post:
      operationId: importCustomerInvoices
      tags: [Customer Invoices]
      summary: Import an invoice with file attached
      description: 'Import a customer invoice with a file attached. Requires scope: customer_invoices:all.'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /customer_invoices/{id}:
    get:
      operationId: getCustomerInvoice
      tags: [Customer Invoices]
      summary: Retrieve a customer invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: updateCustomerInvoice
      tags: [Customer Invoices]
      summary: Update a customer invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /customer_invoices/{id}/finalize:
    put:
      operationId: finalizeCustomerInvoice
      tags: [Customer Invoices]
      summary: Finalize a draft customer invoice
      description: 'Convert a draft invoice or credit note into a finalized one; once finalized it can no longer be edited.'
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /customer_invoices/{id}/mark_as_paid:
    put:
      operationId: markAsPaidCustomerInvoice
      tags: [Customer Invoices]
      summary: Mark a customer invoice as paid
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /customer_invoices/{id}/send_by_email:
    post:
      operationId: sendByEmailCustomerInvoice
      tags: [Customer Invoices]
      summary: Send a customer invoice by email
      description: 'Sends a finalized, imported customer invoice or credit note by email. May return 409 if the PDF is not yet generated; retry shortly.'
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '204': { description: Email queued }
        '409': { $ref: '#/components/responses/Conflict' }
  /customer_invoices/{id}/matched_transactions:
    post:
      operationId: postCustomerInvoiceMatchedTransactions
      tags: [Customer Invoices]
      summary: Match a transaction to a customer invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /supplier_invoices:
    get:
      operationId: getSupplierInvoices
      tags: [Supplier Invoices]
      summary: List supplier invoices
      description: 'Requires scope: supplier_invoices:readonly or supplier_invoices:all.'
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Filter'
      responses:
        '200': { description: OK }
  /supplier_invoices/import:
    post:
      operationId: importSupplierInvoice
      tags: [Supplier Invoices]
      summary: Import a supplier invoice with a file attached
      description: 'Requires scope: supplier_invoices:all.'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /supplier_invoices/{id}:
    get:
      operationId: getSupplierInvoice
      tags: [Supplier Invoices]
      summary: Retrieve a supplier invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: putSupplierInvoice
      tags: [Supplier Invoices]
      summary: Update a supplier invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /supplier_invoices/{id}/validate_accounting:
    put:
      operationId: validateAccountingSupplierInvoice
      tags: [Supplier Invoices]
      summary: Validate the accounting of a supplier invoice
      description: Turn the supplier invoice into a Complete state.
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /quotes:
    get:
      operationId: listQuotes
      tags: [Quotes]
      summary: List quotes
      description: 'Requires scope: quotes:readonly or quotes:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postQuotes
      tags: [Quotes]
      summary: Create a quote
      description: 'Requires scope: quotes:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /quotes/{id}:
    get:
      operationId: getQuote
      tags: [Quotes]
      summary: Retrieve a quote
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /billing_subscriptions:
    get:
      operationId: getBillingSubscriptions
      tags: [Billing Subscriptions]
      summary: List billing subscriptions
      description: 'Requires scope: billing_subscriptions:readonly or billing_subscriptions:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postBillingSubscriptions
      tags: [Billing Subscriptions]
      summary: Create a billing subscription
      description: 'Pennylane generates the customer invoice each month. Requires scope: billing_subscriptions:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /products:
    get:
      operationId: getProducts
      tags: [Products]
      summary: List products
      description: 'Requires scope: products:readonly or products:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postProducts
      tags: [Products]
      summary: Create a product
      description: 'Requires scope: products:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /products/{id}:
    get:
      operationId: getProduct
      tags: [Products]
      summary: Retrieve a product
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: putProduct
      tags: [Products]
      summary: Update a product
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /customers:
    get:
      operationId: getCustomers
      tags: [Customers]
      summary: List customers (company and individual)
      description: 'Requires scope: customers:readonly or customers:all.'
      responses:
        '200': { description: OK }
  /customers/{id}:
    get:
      operationId: getCustomer
      tags: [Customers]
      summary: Retrieve a customer
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /company_customers:
    post:
      operationId: postCompanyCustomer
      tags: [Customers]
      summary: Create a company customer
      description: 'Requires scope: customers:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /individual_customers:
    post:
      operationId: postIndividualCustomer
      tags: [Customers]
      summary: Create an individual customer
      description: 'Requires scope: customers:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /suppliers:
    get:
      operationId: getSuppliers
      tags: [Suppliers]
      summary: List suppliers
      description: 'Requires scope: suppliers:readonly or suppliers:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postSupplier
      tags: [Suppliers]
      summary: Create a supplier
      description: 'Requires scope: suppliers:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /suppliers/{id}:
    get:
      operationId: getSupplier
      tags: [Suppliers]
      summary: Retrieve a supplier
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: putSupplier
      tags: [Suppliers]
      summary: Update a supplier
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /bank_accounts:
    get:
      operationId: getBankAccounts
      tags: [Bank Accounts]
      summary: List bank accounts
      description: 'Requires scope: bank_accounts:readonly or bank_accounts:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postBankAccount
      tags: [Bank Accounts]
      summary: Create a bank account
      description: 'Requires scope: bank_accounts:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /bank_accounts/{id}:
    get:
      operationId: getBankAccount
      tags: [Bank Accounts]
      summary: Retrieve a bank account
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
  /transactions:
    get:
      operationId: getTransactions
      tags: [Transactions]
      summary: List transactions
      description: 'Requires scope: transactions:readonly or transactions:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: createTransaction
      tags: [Transactions]
      summary: Create a banking transaction
      description: 'Requires scope: transactions:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /transactions/{id}:
    get:
      operationId: getTransaction
      tags: [Transactions]
      summary: Retrieve a transaction
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200': { description: OK }
    put:
      operationId: updateTransaction
      tags: [Transactions]
      summary: Update a transaction
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '200': { description: OK }
  /sepa_mandates:
    get:
      operationId: getSepaMandates
      tags: [Mandates]
      summary: List SEPA mandates
      description: 'Requires scope: customer_mandates:readonly or customer_mandates:all.'
      responses:
        '200': { description: OK }
    post:
      operationId: postSepaMandates
      tags: [Mandates]
      summary: Create a SEPA mandate
      description: 'Create a SEPA mandate to enable direct debit payments. Requires scope: customer_mandates:all.'
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /gocardless_mandates:
    get:
      operationId: getGocardlessMandates
      tags: [Mandates]
      summary: List GoCardless mandates
      description: 'Requires scope: customer_mandates:readonly or customer_mandates:all.'
      responses:
        '200': { description: OK }
  /file_attachments:
    post:
      operationId: postFileAttachments
      tags: [File Attachments]
      summary: Upload a file
      description: >-
        Upload a file to attach to any resource that provides a file_attachment_id.
        Maximum file size 100MB. Requires scope: file_attachments:all.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema: { type: object }
      responses:
        '201': { description: Created }
  /customer_invoices/changes:
    get:
      operationId: getCustomerInvoicesChanges
      tags: [Change Events]
      summary: Get customer invoice change events
      description: >-
        Returns the list of changes from the provided start_date (last 4 weeks retained),
        ordered by processed_at ASC. Requires scope: customer_invoices:readonly or customer_invoices:all.
      parameters:
        - name: start_date
          in: query
          schema: { type: string, format: date-time }
      responses:
        '200': { description: OK }
  /supplier_invoices/changes:
    get:
      operationId: getSupplierInvoicesChanges
      tags: [Change Events]
      summary: Get supplier invoice change events
      parameters:
        - name: start_date
          in: query
          schema: { type: string, format: date-time }
      responses:
        '200': { description: OK }
  /pa_registrations:
    get:
      operationId: getPaRegistrations
      tags: [E-Invoicing]
      summary: List PA (Plateforme Agréée) registrations
      description: >-
        Returns all PA registrations for the company, including activation status and
        exchange direction, for French e-invoicing onboarding. Requires scope: pa_registrations:readonly.
      responses:
        '200': { description: OK }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: token
      description: >-
        Bearer token in the Authorization header. The token may be a Company API token,
        a Firm API token, or an OAuth 2.0 access token obtained via
        https://app.pennylane.com/oauth/authorize and /oauth/token.
  parameters:
    PathId:
      name: id
      in: path
      required: true
      description: Internal Pennylane resource id (v2 uses internal ids only, not source_id).
      schema: { type: integer }
    Cursor:
      name: cursor
      in: query
      required: false
      description: Opaque cursor for cursor-based pagination.
      schema: { type: string }
    Limit:
      name: limit
      in: query
      required: false
      description: Page size.
      schema: { type: integer, default: 100 }
    Filter:
      name: filter
      in: query
      required: false
      description: JSON filter expression applied on listing endpoints.
      schema: { type: string }
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Conflict:
      description: Conflict (e.g. PDF not yet generated)
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    UnprocessableEntity:
      description: Validation error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    TooManyRequests:
      description: >-
        Rate limit exceeded (25 requests per 5 seconds per token). Includes
        retry-after, ratelimit-limit, ratelimit-remaining, and ratelimit-reset headers.
      headers:
        retry-after:
          schema: { type: integer }
        ratelimit-limit:
          schema: { type: integer }
        ratelimit-remaining:
          schema: { type: integer }
        ratelimit-reset:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    Error:
      type: object
      description: Consistent JSON error schema returned across the API.
      properties:
        status:
          type: integer
        message:
          type: string
        details:
          type: object
          description: Actionable field-level information, most useful for 422 validation errors.