> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chift.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Pennylane

export const OverviewLegend = ({showTitle = true}) => <blockquote>
    {showTitle && <p>
        <strong>Overview legend 🧭</strong>
      </p>}
    <table>
      <thead>
        <tr>
          <th>Column</th>
          <th>Value</th>
          <th>Meaning</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Geography</td>
          <td>🇫🇷 FR · 🇧🇪 BE · …</td>
          <td>Countries where the connector is officially supported.</td>
        </tr>
        <tr>
          <td rowSpan={3}>Software type</td>
          <td>SaaS</td>
          <td>Through API.</td>
        </tr>
        <tr>
          <td>On-premise (local agent)</td>
          <td>
            Software running locally — installation of a local agent is
            required.
          </td>
        </tr>
        <tr>
          <td>On-premise (API)</td>
          <td>Software running locally — API available.</td>
        </tr>
        <tr>
          <td rowSpan={2}>Status</td>
          <td>🔵 Live</td>
          <td>Connector is generally available and production-ready.</td>
        </tr>
        <tr>
          <td>🟣 Beta</td>
          <td>
            Connector is in beta — usable in production but may still evolve.
          </td>
        </tr>
        <tr>
          <td>Multi folder</td>
          <td>✅ Yes / ❌ No</td>
          <td>
            Connection to multiple accounting folders at the same time (see{' '}
            <a href="/developer-guides/api-guides/accounting/folders">
              accounting folders guide
            </a>
            ).
          </td>
        </tr>
        <tr>
          <td>Rate limits</td>
          <td>✅ No / ❌ Yes</td>
          <td>Whether the target software sets rate limits on API calls.</td>
        </tr>
        <tr>
          <td rowSpan={3}>API keys</td>
          <td>❎ No</td>
          <td>
            No API keys required (OAuth2 client secret and client ID). No
            requirements to activate the connector — you can create a
            connection.
          </td>
        </tr>
        <tr>
          <td>🔑</td>
          <td>
            Keys are required to activate the connector. Chift cannot act as
            intermediary to obtain them; we can still assist with steps to get
            keys directly from the software provider.
          </td>
        </tr>
        <tr>
          <td>🔑 ☑️ via Chift</td>
          <td>
            Keys are required to activate the connector. You can go through
            Chift to get the keys (intermediary or partnership keys). An
            approval process may still apply, but you do not need to request
            keys from the vendor on your own.
          </td>
        </tr>
        <tr>
          <td rowSpan={3}>Approval / certification process</td>
          <td>⚡ Instant</td>
          <td>
            No keys required, or Chift can encode their keys for you when
            requested. Activation is instantaneous.
          </td>
        </tr>
        <tr>
          <td>🟢 Approval — [Duration]</td>
          <td>
            Approval will be granted. The vendor may require information or app
            configuration in a developer portal before issuing keys.
          </td>
        </tr>
        <tr>
          <td>🟠 Approval — [Duration]</td>
          <td>
            Approval is not guaranteed — often due to integration strategy; the
            vendor may do a deeper assessment (competitors, partnership
            requirements, and similar).
          </td>
        </tr>
        <tr>
          <td rowSpan={2}>Activation time</td>
          <td>⚡ Instant</td>
          <td>
            If no keys are required, or Chift has keys ready to share with you.
          </td>
        </tr>
        <tr>
          <td>Time</td>
          <td>
            Estimated time to get the connector activated in production as a
            result of the approval or certification process (e.g. ⏱️ 2 days, 1
            week).
          </td>
        </tr>
        <tr>
          <td rowSpan={3}>Extra fees — software editor</td>
          <td>❎ No</td>
          <td>No fees charged by the software editor.</td>
        </tr>
        <tr>
          <td>💰 🕹️</td>
          <td>Fees associated with obtaining a testing account.</td>
        </tr>
        <tr>
          <td>💰 🔑</td>
          <td>Fees charged to get API keys.</td>
        </tr>
        <tr>
          <td rowSpan={2}>Extra fees — end user</td>
          <td>❎ No</td>
          <td>The end user does not pay extra to get integrated.</td>
        </tr>
        <tr>
          <td>💰 Yes</td>
          <td>The end user must pay extra to get integrated.</td>
        </tr>
        <tr>
          <td>Comments on costs</td>
          <td>—</td>
          <td>Additional notes on fees or pricing when relevant.</td>
        </tr>
        <tr>
          <td rowSpan={4}>Sandbox account</td>
          <td>✅ via Chift</td>
          <td>Chift can provide you with a sandbox.</td>
        </tr>
        <tr>
          <td>🟠 Only through integrator</td>
          <td>Only the software's integrator can provide a sandbox.</td>
        </tr>
        <tr>
          <td>✅ Self-service</td>
          <td>You can create your own sandbox.</td>
        </tr>
        <tr>
          <td>✅ Trial account</td>
          <td>It is possible to create a trial account.</td>
        </tr>
      </tbody>
    </table>
  </blockquote>;

export const ConnectorCardIframe = ({api = 'accounting', connectors}) => {
  const [theme, setTheme] = React.useState('light');
  React.useEffect(() => {
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const queryParams = new URLSearchParams({
    api,
    theme,
    ...connectors ? {
      connectors
    } : {}
  });
  const iframeUrl = `https://chift-coverage-matrix.s3.eu-west-3.amazonaws.com/connector-card.html?${queryParams.toString()}`;
  return <iframe src={iframeUrl} title={`Chift connector information - ${api}`} style={{
    display: 'block',
    width: '100%',
    height: '480px',
    margin: 0,
    padding: 0,
    border: 'none'
  }} />;
};

export const CoverageIframe = ({api = 'accounting', connectors}) => {
  const [theme, setTheme] = React.useState('light');
  const [isFullscreen, setIsFullscreen] = React.useState(false);
  const [currentIframeUrl, setCurrentIframeUrl] = React.useState(null);
  const iframeRef = React.useRef(null);
  React.useEffect(() => {
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  React.useEffect(() => {
    const handleFullscreenChange = () => {
      setIsFullscreen(!!document.fullscreenElement);
    };
    document.addEventListener('fullscreenchange', handleFullscreenChange);
    return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
  }, []);
  React.useEffect(() => {
    const handleMessage = event => {
      if (!event.origin.includes('chift-coverage-matrix.s3.eu-west-3.amazonaws.com')) return;
      if (event.data?.type === 'urlChange' && event.data?.url) {
        setCurrentIframeUrl(event.data.url);
      }
    };
    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);
  const queryParams = new URLSearchParams({
    api,
    theme,
    ...connectors ? {
      connectors
    } : {}
  });
  const iframeUrl = `https://chift-coverage-matrix.s3.eu-west-3.amazonaws.com/coverage.html?${queryParams.toString()}`;
  const openUrl = currentIframeUrl || iframeUrl;
  const toggleFullscreen = () => {
    if (!document.fullscreenElement) {
      iframeRef.current?.requestFullscreen();
    } else {
      document.exitFullscreen();
    }
  };
  const isDark = theme === 'dark';
  const buttonStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: '8px',
    padding: '4px 12px',
    fontSize: '14px',
    fontWeight: '500',
    color: isDark ? '#d4d4d4' : '#374151',
    backgroundColor: 'transparent',
    border: `1px solid ${isDark ? '#404040' : '#e5e7eb'}`,
    borderRadius: '12px',
    cursor: 'pointer',
    textDecoration: 'none',
    transition: 'all 0.15s ease'
  };
  const hoverBg = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.03)';
  const hoverBorder = isDark ? '#525252' : '#d1d5db';
  const defaultBg = 'transparent';
  const defaultBorder = isDark ? '#404040' : '#e5e7eb';
  return <>
  <div style={{
    display: 'flex',
    justifyContent: 'flex-end',
    gap: '12px',
    marginBottom: '8px'
  }}>
    <button onClick={toggleFullscreen} style={buttonStyle} onMouseEnter={e => {
    e.target.style.backgroundColor = hoverBg;
    e.target.style.borderColor = hoverBorder;
  }} onMouseLeave={e => {
    e.target.style.backgroundColor = defaultBg;
    e.target.style.borderColor = defaultBorder;
  }}>
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        {isFullscreen ? <>
            <polyline points="4 14 10 14 10 20"></polyline>
            <polyline points="20 10 14 10 14 4"></polyline>
            <line x1="14" y1="10" x2="21" y2="3"></line>
            <line x1="3" y1="21" x2="10" y2="14"></line>
          </> : <>
            <polyline points="15 3 21 3 21 9"></polyline>
            <polyline points="9 21 3 21 3 15"></polyline>
            <line x1="21" y1="3" x2="14" y2="10"></line>
            <line x1="3" y1="21" x2="10" y2="14"></line>
          </>}
      </svg>
      {isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
    </button>
    <a href={iframeUrl} target="_blank" rel="noopener noreferrer" style={buttonStyle} onMouseEnter={e => {
    e.target.style.backgroundColor = hoverBg;
    e.target.style.borderColor = hoverBorder;
  }} onMouseLeave={e => {
    e.target.style.backgroundColor = defaultBg;
    e.target.style.borderColor = defaultBorder;
  }}>
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
        <polyline points="15 3 21 3 21 9"></polyline>
        <line x1="10" y1="14" x2="21" y2="3"></line>
      </svg>
      Open in new tab
    </a>
  </div>
  <iframe ref={iframeRef} src={iframeUrl} title={`Chift Coverage Matrix - ${api}`} style={{
    height: 'max(500px, 80vh)'
  }} className="w-full" allowFullScreen />
  <blockquote>
    <p>
      <strong>Matrix Legend 🧭</strong>
    </p>
    <table>
      <thead>
        <tr>
          <th>Status</th>
          <th>Meaning</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>✅ Implemented</td>
          <td>Endpoint is implemented and available.</td>
        </tr>
        <tr>
          <td>❌ Not supported</td>
          <td>
            Endpoint is not supported by the target software (connector
            limitation). Cannot be implemented.
          </td>
        </tr>
        <tr>
          <td>💬 On request</td>
          <td>
            Endpoint is not implemented but feasibility is validated. Can be
            implemented on request — contact your Chift point of contact to
            discuss scope and timing.
          </td>
        </tr>
        <tr>
          <td>🔎 To be analyzed</td>
          <td>
            Endpoint is not implemented and feasibility has not yet been fully
            assessed. Analysis is pending.
          </td>
        </tr>
      </tbody>
    </table>
  </blockquote>
</>;
};

<ConnectorCardIframe api="accounting" connectors="Pennylane" />

<Accordion title="Overview Legend 🧭">
  <OverviewLegend showTitle={false} />
</Accordion>

# Introduction

Pennylane centralizes in real time all the financial flows of companies and facilitates the collaboration between managers and their accountant. Deliver the Pennylane integration your customers want in no time.

## Configure Pennylane

<Info>
  Sandbox and production credentials are identical on Pennylane's side. Before going live, your integration must be certified by both Chift and Pennylane (see Step 3).
</Info>

Follow the steps below to get sandbox access, retrieve your OAuth2 credentials, and go live. Chift can assist you with each step.

### Step 1: Open the partnership & get sandbox access

Fill in the [partnership form](https://www.notion.so/23b2276c03bf8059a59dcb775b546a96?pvs=21):

* **Question 4** — Which entity will be responsible for developing the connector? Select **Chift**.
* **Question 5** — Do you have access to the Pennylane sandbox? Select **No, open access for me**. Question 5b will then appear; enter the email address to associate with the sandbox.

<Tip>
  By default, Pennylane provides sandboxes **without the accounting module**. Once you're connected to your sandbox, notify Chift so we can ask Pennylane to activate it for you.
</Tip>

### Step 2: Get your OAuth2 credentials

Fill in the [OAuth2 request form](https://form.typeform.com/to/Vn0iWTJv) to receive your **Client ID** and **Client Secret**.

### Step 3: Go live — certify your integration

Fill in the [pre-certification form](https://www.notion.so/23e2276c03bf80bc9704c06e99cd059c?pvs=21), then ask Chift to schedule a meeting with Pennylane. During this meeting you'll:

* Present an internal demo of your integration to Pennylane.
* Have Chift on the call to answer any technical questions.

### Step 4: Publish on Pennylane's marketplace & in-app

To appear on Pennylane's public marketplace and in-app, and to benefit from their communication around your partnership, fill in the [app-listing form](https://www.notion.so/2402276c03bf80eea025ffdd8d1b214a?pvs=21).

## Test Pennylane

You will receive sandbox access directly after filling in the partnership form from *Step 1*. Don't forget to reach out to your Chift point of contact to activate the Accounting module.

## Connect Pennylane

To activate a connection with Pennylane, users will have to go through the following steps. French article: [Help Center - Pennylane FR](https://help.chift.app/articles/9277286785-pennylane?lang=fr) English article: [Help Center - Pennylane EN](https://help.chift.app/articles/9277286785-pennylane?lang=en)

## Rate limits

Pennylane enforces rate limits on **both production and sandbox** environments, applied **at the token level** (per OAuth-generated token, or per developer token).

* **25 requests every 5 seconds** per token.

For details, see the [Pennylane API v2 rate limiting documentation](https://pennylane.readme.io/docs/rate-limiting-1).

## Technical limitation

Endpoints related to invoice retrieval for Pennylane are subject to a technical limitation concerning associated payments. Pennylane’s API does not expose detailed payment records for an invoice. As a result, the payment information returned consists of a single aggregated payment, where the amount corresponds to the total paid and the payment date is set to the invoice’s due date.

## Coverage

<CoverageIframe api="accounting" connectors="Pennylane" />

## Troubleshooting

### Specific errors for Pennylane:

| Error Code                                   | Error description                                                                                                                                                                                   | Resolution                                                                                                                                                                               |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ERROR\_SUPPLIER\_INVOICE\_PDF\_REQUIRED      | PDFs are mandatory for invoices on Pennylane.                                                                                                                                                       | <ol><li>Make sure that each invoice has an attachment.</li><li>If you are using the sync option, block the option to create an invoice when no PDF is attached to the invoice.</li></ol> |
| ERROR\_CONNECTOR\_AUTHENTICATION             | The token used to connect to Pennylane is not valid anymore.                                                                                                                                        | Reconnect Pennylane to your existing consumer to generate a new authentication token.                                                                                                    |
| ERROR\_BOOKYEAR\_NOT\_FOUND                  | The accounting system is not set up for this date.                                                                                                                                                  | Create a fiscal year in the accounting system including the date of the invoice.                                                                                                         |
| ERROR\_BOOKYEAR\_CLOSED                      | The bookyear/fiscal year is already closed in the accounting system                                                                                                                                 | The fiscal year is closed, it must be reopened or the start date of the synchronization must be adapted accordingly.                                                                     |
| ERROR\_INVOICE\_NUMBER\_ALREADY\_USED        | The sent invoice number is already attributed to an existing invoice in Pennylane                                                                                                                   | Invoice seems to be already imported in Pennylane through another synchronization. Please contact the customer to see how this is possible.                                              |
| UNEXPECTED\_PENNYLANE\_ERROR                 | The pdf received already exists in the application on another invoice                                                                                                                               | Invoice seems to be already imported in Pennylane through another synchronization. Please contact the customer to see how this is possible.                                              |
| UNEXPECTED\_PENNYLANE\_ERROR                 | It’s not possible to create any more invoices on that folder                                                                                                                                        | Account is not active anymore or blocked. Contact the customer to see if the account is still active + if date is not in a closed book/fiscal year.                                      |
| UNEXPECTED\_PENNYLANE\_ERROR                 | Invoice lines quantity must be different than 0, Accounting frozen until Given Date (e.g., "Invoice lines quantity doit être différent de 0, La comptabilité est figée jusqu’au 30 septembre 2024") | Please unfreeze accounting/books on Pennylane.                                                                                                                                           |
| UNEXPECTED\_PENNYLANE\_ERROR                 | Invoice lines quantity must be different than 0                                                                                                                                                     | Pennylane API doesn’t allow lines with quantity 0. Chift will bypass those lines in the future.                                                                                          |
| UNEXPECTED\_PENNYLANE\_ERROR                 | The setup of the invoice tab is incomplete on Pennylane.                                                                                                                                            | Setup an invoicing sequence for customer invoices in Pennylane => To be done in Pennylane so that Pennylane knows how to generate invoice number                                         |
| ERROR\_CONNECTOR\_CONFIGURATION              | The invoice settings of Pennylane must be correctly set up (invoicing sequences must be defined) before creating invoices via API.                                                                  | Set up invoicing sequences in Pennylane settings.                                                                                                                                        |
| ERROR\_DRAFT\_SUPPLIER\_INVOICE              | You cannot create a draft supplier invoice in Pennylane.                                                                                                                                            | Set the invoice status to validated/posted instead of draft.                                                                                                                             |
| ERROR\_INVALID\_BODY                         | Email addresses must be valid, or ledger account numbers must be at least 3 digits (without counting trailing zeros).                                                                               | Provide a valid email address and use account numbers with at least 3 digits.                                                                                                            |
| ERROR\_INVALID\_FIELD\_FORMAT\_BANK\_ACCOUNT | Pennylane validates the bank account/IBAN format.                                                                                                                                                   | Provide a valid bank account number or IBAN.                                                                                                                                             |
| ERROR\_PDF\_MUST\_BE\_UNIQUE                 | Pennylane performs a uniqueness check on attached PDFs.                                                                                                                                             | Ensure each PDF attachment is unique and not already attached to another invoice.                                                                                                        |
| ERROR\_INVOICE\_DATE\_IN\_FUTURE             | You cannot create an invoice with a future date without attaching a PDF.                                                                                                                            | Attach a PDF when creating an invoice with a future date.                                                                                                                                |
| ERROR\_INVOICE\_DATE\_IN\_PAST               | You cannot create an invoice with a date before the date of the last validated invoice in Pennylane.                                                                                                | Use a date equal to or after the last validated invoice date.                                                                                                                            |
