Point of Sales use case
Connect your POS to Pennylane
Please use the Oauth 2.0 process to connect your customers through the integration.
Please note that our users' API tokens are intended for personal use, not for use by partners. Your app will not be published in our official Marketplace if it doesn't use OAuth.
A note about IDs
Please note that each company has its own IDs. The ID of ledger accounts (comptes comptables) are different for each company. You need to store and use the company's own IDs.
Identify the right journal
1. List journals and allow your users to choose the Journal they use for their POS entries.
Retrieve journals
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/journals?page=1&per_page=100' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Response :
{
"total_pages": 1,
"current_page": 1,
"per_page": 20,
"total_items": 14,
"items": [
{
"code": "VT",
"id": 1389447,
"label": "Journal de vente"
},
{
"code": "HA",
"id": 1389448,
"label": "Journal d'achat"
},
........
Save the ID and use it for every entry you post.
It may happen that no “Caisse” type journal exists. To create one, follow the next step ⬇️
2. If there is no journal to use for POS, allow your user to Create a journal
Post the code and the label. Values must be understandable by accountants _(exemple : Journal de Caisse, Journal de la caisse XXX, Caisse XXX, ...)_
curl --request POST \
--url https://app.pennylane.com/api/external/v2/journals \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx' \
--header 'content-type: application/json' \
--data '
{
"code": "POSXX",
"label": "Journal de caisse XXX"
}
Response :
{
"code": "POSXX",
"id": 6350564,
"label": "Journal de caisse XXX"
}
Save the ID and use it for every entry you post
For this example, the
journal_id
to use to create entries is6350564
Create your accounting entry layout

1. Ledger accounts for Net income
Net income
Revenues are posted into "706xxx" ledger accounts, which should be differentiated by VAT rate to simplify accounting.
The most common ledger accounts are the following :
- Revenues with 20% VAT : 7062
- Revenues with 10% VAT : 7061
Payload example :
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22706%22%7D%5D' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Response :
{
"total_pages": 1,
"current_page": 1,
"per_page": 1000,
"total_items": 53,
"items": [
{
"id": 263389185,
"number": "7061",
"label": "Prestations de services",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
{
]
}
Give to your customers the possibility to match ledger accounts with the type of revenue
For this example, the
ledger_account_id
to use for 10% VAT revenue is263389185
If a ledger account doesn't exist you can create it.
Payload example : the ledger account 7062 is missing
curl --request POST \
--url https://app.pennylane.com/api/external/v2/ledger_accounts \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
--header 'content-type: application/json' \
--data '
{
"number": "7062",
"label": "Ventes à 20%"
}
'
Response :
{
"id": 1118517253,
"number": "7062",
"label": "Ventes à 20%",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
}
You can now use the new ledger account
For this example, the
ledger_account_id
to use for 20% VAT is1118517253
2. Ledger accounts for VAT
VAT
VAT amounts are posted into "4457xxx" ledger accounts, which should be differentiated by VAT rate to simplify accounting.
The most common VAT ledger accounts in metropolitan France are :
- 20% VAT : 44571009
- 10% VAT : 44571008
- 5,5% VAT : 44571006
The most common VAT ledger accounts in french DOM-TOM are :
- 8,5% VAT : 44571007
- 5,5% VAT : 44571006
- 2,1% VAT : 44571005
Payload example :
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%2244571%22%7D%5D' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Response :
{
"total_pages": 1,
"current_page": 1,
"per_page": 1000,
"total_items": 7,
"items": [
{
"id": 263381975,
"number": "44571008",
"label": "TVA collectée à 10%",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
{
"id": 263381977,
"number": "44571009",
"label": "TVA collectée à 20%",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
You can directly associate your VAT and related ledger account IDs.
For this example, the
ledger_account_id
to use for 10% VAT is263381975
2.2 Create VAT ledger accounts
There is almost no case were you will have to create a VAT ledger account, as long as they exists by default on every company on Pennylane.
Otherwise, please contact your partnership manager to talk about it.
3. Ledger accounts for Type of payments
Type of payments
Payment types are posted into "511xxx" ledger accounts, and don't need to be differentiated by VAT.
The most common ledger accounts are the following :
- Checks : 5112
- Uber eats : 511008
- Cash : 530
Payload example for Cash :
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%2253%22%7D%5D' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Payload example for other :
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22511%22%7D%5D' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Response :
{
"total_pages": 1,
"current_page": 1,
"per_page": 1000,
"total_items": 6,
"items": [
{
"id": 263382102,
"number": "511008",
"label": "Encaissements Uber Eats",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
{
"id": 263382104,
"number": "5112",
"label": "Chèques à encaisser",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
Give to you customers the possibility to match ledger accounts with the type of payments
For this example, the
ledger_account_id
to use for Uber Eats payments is263382102
If a ledger account doesn't exist you can create it.
Payload example : the ledger account for "Deliveroo" is missing
curl --request POST \
--url https://app.pennylane.com/api/external/v2/ledger_accounts \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx' \
--header 'content-type: application/json' \
--data '
{
"number": "5110012",
"label": "Deliveroo"
}
'
Response :
{
"id": 1118517352,
"number": "5110012",
"label": "Deliveroo",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
}
You can now use the new ledger account
For this example, the
ledger_account_id
to use for Deliveroo payment type is1118517352
Anticipate rounding differences
Every rounding issue or unexpected differences have to be handled.
The sum of Revenue and VAT are in
Credit
side
The sum of Payment type are inDebit
side
The sum ofCredit
andDebit
must be equal.If not, you have to add an entry line to balance the entry.
If the
Credit
is less than theDebit
, you must add a line in theCredit
with the ledger account758
.
For the opposite, use the658
onDebit
Payload example to retrieve the ID of ledger_account
658
curl --request GET \
--url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22658%22%7D%5D' \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx'
Response :
{
"total_pages": 1,
"current_page": 1,
"per_page": 1000,
"total_items": 7,
"items": [
{
"id": 263382333,
"number": "658",
"label": "Pénalités et autres charges (anciennement Charges diverses de gestion courante)",
"vat_rate": "any",
"country_alpha2": "any",
"enabled": true
},
You can directly associate your VAT and related ledger accounts.
For this example, the
ledger_account_id
to use to balance the entry on debit is263382333
Let's analyze a simple 'Ticket Z' ⬇️

Payload for this example :
Date
: The date must be the date of the Ticket ZLabel
: Please provide something complete and comprehensible. Suggestion : "Ticket Z - anything that clearly identifies the POS - Date"journal_id
: Use the ID of the journal you just retrieved or createdledger_attachment_id
: You can also upload the Ticket Z file using Upload a file before - it is optionalledger_entry_lines
: 1 array of objects perledger_account_id
ledger_entry_lines:debit
: Type of payment values and potential rounding differences. It can also be negative values of Net income and VATledger_entry_lines:credit
: Net income and VAT values and potential rounding differences. It can also be negative values of Type of payment valuesledger_entry_lines:ledger_account_id
: The account corresponding to the line you are creating
curl --request POST \
--url https://app.pennylane.com/api/external/v2/ledger_entries \
--header 'accept: application/json' \
--header 'authorization: Bearer xxx' \
--header 'content-type: application/json' \
--data '
{
"date": "2025-01-01",
"label": "Ticket Z - Restaurant Test - 01-01-2025",
"journal_id": 6350564,
"ledger_attachment_id": 32325335
"ledger_entry_lines": [
{
"debit": "0",
"credit": "90.91",
"ledger_account_id": 263389185
},
{
"debit": "0",
"credit": "9.09",
"ledger_account_id": 263381975
},
{
"debit": "99.99",
"credit": "0",
"ledger_account_id": 263382102
},
{
"debit": "0.01",
"credit": "0",
"ledger_account_id": 263382333
}
]
}
'
Response :
{
"label": "Ticket Z - Restaurant Test - 01-01-2025",
"date": "2025-01-01",
"journal_id": 6350564,
"ledger_attachment_id": 32325335
"id": 1753102653001,
"ledger_attachment_id": null,
"ledger_attachment_filename": null,
"ledger_entry_lines": [
{
"id": 7222323527,
"debit": "0.0",
"credit": "90.91",
"label": "",
"ledger_account_id": 263389185
},
{
"id": 7222323528,
"debit": "0.0",
"credit": "9.09",
"label": "",
"ledger_account_id": 263381975
},
{
"id": 7222323529,
"debit": "99.99",
"credit": "0.0",
"label": "",
"ledger_account_id": 263382102
},
{
"id": 7222323530,
"debit": "0.01",
"credit": "0.0",
"label": "",
"ledger_account_id": 263382333
}
]
}
If your entry is not balanced, yo will receive an error :
Please check the Anticipate rounding differences part
{
"status": 422,
"error": "Entry lines are not balanced"
}
Congratulations, your entry has been successfully registered!
Please keep the information on your side to know if a Ticket Z has been correctly registered or not.
If you retry the same payload, nothing will prevent a double entry on Pennylane - You are responsible for the data quality that you send.
If your entry doesn't get a 201 response, it hasn't been registered and you need to try again.
Updated about 1 month ago