Record payments
An issued invoice is settled only once its payment is recorded. By recording each payment in Scribee, you satisfy two obligations with a single entry: the invoice's collection status, which automatically moves to 212 Collected as soon as the invoice's settled amount reaches the amount due for payment, and the e-reporting declaration of payments for B2C and international B2B services invoices whose VAT is due on collection, when the issuing company is subject to that obligation. Your system captures the payment; Scribee derives the rest.
What Scribee does for you
- Automatic collection status: when the invoice's settled amount reaches
payable_amount, the invoice moves to212Collected, from any of the statuses where recording a payment is accepted - intermediate statuses are optional, an invoice in200Deposited status moves directly to212. If a correction or a deletion pushes the total back below that amount, the invoice reverts to211Payment sent. - The amount to settle is the amount due for payment, not the total including tax: Scribee measures settlement against
payable_amount(BT-115, the amount due for payment), not againsttax_inclusive_amount(BT-112, the total including tax). On an invoice that carries no already-paid amount - the vast majority - the two values are equal and nothing changes for you. They diverge as soon as part of the invoice was collected at issuance: that amount is exposed underprepaid_amount(BT-113), andpayable_amountcarries only the rest. An invoice of35.11including tax whoseprepaid_amountis20.00has apayable_amountof15.11: it is settled by a15.11payment, andremaining_amountstarts at15.11, not35.11. If you reconcile your payments againstremaining_amount, compare it topayable_amount, never totax_inclusive_amount. - Deducted advances include their VAT: on an invoice from an accounting integration,
prepaid_amountrepresents the amount already paid including VAT in the invoice currency. For example, an advance of100.00excluding tax with20.00VAT represents120.00already paid. For an invoice total of240.00including tax without rounding,payable_amountis then120.00. Invoice VAT remains calculated before the advance is deducted. - A
payable_amountof0is a fully prepaid invoice: it is not a missing value.remaining_amountis then0from issuance and the invoice is considered settled without any payment being recorded. On invoices deposited by a Scribee accounting integration - never on an invoice created or imported through the partner API, even one declared already paid (Issue a sales invoice) - the move to212Collected is asynchronous: it happens once the invoice becomes eligible, never in the response to a call, and a status read straight afterwards can still be the previous one. The matching212event is dated from the collection; the collected amount is read underprepaid_amounton the invoice, never on the lifecycle event, and the payment list stays empty. The only missing-value case is apayable_amountofnull, which some imported invoices do not carry: Scribee then falls back totax_inclusive_amount. - The settled amount is not always the sum of your payments: on workspaces where early-payment discount handling is enabled, an applied discount counts toward the settled amount just like a payment. The invoice can therefore move to
212while the invoice'spaid_totalfield stays belowpayable_amount. The authoritative field isremaining_amount, which accounts for both; do not recompute the balance frompaid_totalalone. - Inherited currency:
currency_codeis never sent and cannot be changed; each payment takes on the invoice's currency, server-side. - Payment declaration kept up to date: for a sales invoice whose VAT tax point code is absent, equal to
72, or equal to432- the two codes name the same tax point, on two UNTDID lists -, and whose issuing company is subject to the payment reporting obligation on the payment date, every payment creation, correction, or deletion is reflected in the period's e-reporting declaration, broken down by VAT rate (see Declare payments). All three conditions must hold: any othertax_due_date_code, any purchase invoice, and any VAT regime without a payment reporting obligation are excluded - with no error and no signal on the API side. A sale whose VAT breakdowns are all in categoryGorO, not subject to VAT in France, is excluded the same way. Correction and deletion do not behave the same way once the obligation goes away: moving an already-declared payment to a date not subject to it leaves the line on its original report, whereas deletion always removes it (Declare payments). Only the sales that e-reporting covers are declared - an international B2B sale, whose buyer is identified without a SIREN or carries a SIREN but is established outside the French VAT territory, or a B2C sale, whose buyer carries no identifier - and some sales are excluded as well whatever their tax-point code: a buyer identified by a SIREN or a SIRET and established in the French VAT territory, whose212Collected status declares the collection; a seller established in French Guiana, Mayotte, an overseas collectivity or the TAAF; a wholly reverse-charged sale; a delivery of goods. A sale is declared only for its breakdowns other thanAE,GandO, a double invoice only for its services share, and amounts are declared in euros, at the payment date's rate. When the declaration cannot be established, the payment is recorded with no declaration line, with no signal on the API side (Declare payments).
Record a payment
This call creates a payment on the invoice, in your production account. Nothing is transmitted to the invoice's recipient. For a sales invoice covered by the payments declaration (see above), the payment is added to the draft of its period's e-reporting declaration.
POST /api/v1/invoices/{invoice_id}/payments accepts amount and payment_date (required), plus payment_means_code, reference, note, payer_role, and allocations (optional, the last two described below). The token must carry the read write scopes. The invoice must be in one of the following statuses: 200 Deposited, 202 Received, 203 Made available, 204 Taken in charge, 205 Approved, 206 Partially approved, 207 Disputed, 208 Suspended, 211 Payment sent, or 212 Collected. Any other status - including 000 Draft, 210 Refused, 213 Rejected, 220 Cancelled, and 221 Routing error - is refused.
curl -X POST https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"amount": 1000.0,
"payment_date": "2025-01-10",
"payment_means_code": "30",
"reference": "VIR-2025-0110"
}
}'
The response is a 201 Created:
{
"data": {
"id": 87,
"amount": 1000.0,
"payment_date": "2025-01-10",
"payment_means_code": "30",
"currency_code": "EUR",
"reference": "VIR-2025-0110",
"note": null,
"payer_role": "buyer",
"allocations": [],
"created_at": "2025-01-10T09:15:00Z"
}
}
payment_means_code follows the UNTDID 4461 list restricted to the values 1, 10, 20, 30, 31, 48, 49, 57, 58, 59, and 97 - notably 30 bank transfer, 48 payment card, 49 direct debit, 58 SEPA credit transfer, 59 SEPA direct debit.
Partial payments are recorded the same way: several calls, one payment each. The invoice's status changes only once the cumulative total reaches the amount due for payment (payable_amount).
Every payment nonetheless writes a 212 lifecycle event carrying its own amount, never the running paid total: Annexe 7 rule P1.15 declares the sum received at each collection, and a partial payment is a collection. So the two cases read differently:
- A payment that settles the invoice fires the
collecttransition:lifecycle_statemoves tocollectedand the event carries that payment's amount. - A partial payment writes its
212event without movinglifecycle_state.
A 212 event in the history is therefore not enough to conclude that the invoice is collected - lifecycle_state is what counts. If you would rather declare the collection yourself than derive it from a payment, the transition endpoint accepts collect with a collected_amount field; see The invoice lifecycle.
Transmission of the 212 event to the PPF (Portail Public de Facturation) is reserved for invoices whose VAT is due on collection: tax_due_date_code at 72 (or 432), any retainer invoice, or, without a tax_due_date_code, any invoice whose invoicing framework (BT-23) is not a goods framework (a code starting with B). An invoice whose tax_due_date_code is 5, 3, 29 or 35 does not declare its 212 events to the PPF.
State the payer and the split by rate
By default, a payment is taken to be paid by the buyer, and Scribee itself spreads the amount of its 212 event across the invoice's VAT rates, pro rata. Two optional fields replace those two assumptions when they do not hold, typically when a share of the invoice is paid by a third party - an insurer, a body paying a subsidy, a set-off - and that share appears on the invoice under prepaid_amount (BT-113).
payer_role:buyer(the default) orthird_party_payer.third_party_payerdesignates the third party paying the share carried byprepaid_amount. The third party is not identified in the payment.allocations: a list ofvat_rate/amountblocks. When provided, it is exactly the split that this payment's212event declares, in place of the pro rata. When absent, the pro rata applies. Every payment returnspayer_roleandallocations, the latter empty when no split was provided.
Example: an invoice of 1200.00 including VAT at the 20 % rate, of which an insurer covers 900.00. It carries prepaid_amount at 900.00, hence payable_amount at 300.00. The buyer pays its 300.00 by declaring the total at the invoice's rate, minus the third party's share at rate 0:
curl -X POST https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"amount": 300.0,
"payment_date": "2025-01-10",
"payer_role": "buyer",
"allocations": [
{ "vat_rate": 20, "amount": 1200.0 },
{ "vat_rate": 0, "amount": -900.0 }
]
}
}'
The insurer pays its share, declared at rate 0:
curl -X POST https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"amount": 900.0,
"payment_date": "2025-01-12",
"payer_role": "third_party_payer",
"allocations": [
{ "vat_rate": 0, "amount": 900.0 }
]
}
}'
The following rules are checked on recording. A breach is refused with 422 and nothing is recorded:
- The blocks'
amountvalues add up exactly to the payment'samount. - Each
vat_rateis a rate carried by the invoice, or0. - A given
vat_rateappears at most once per sign: at rate0, one positive block and one negative block may coexist; no other rate repeats. - Each block
amountis non-zero, has at most two decimals, and can only be negative at rate0. - For the buyer, negative blocks at rate
0deduct the third party's share: their total, across all of the buyer's payments on the invoice, does not exceedprepaid_amount. - For
third_party_payer, the invoice must carry a strictly positiveprepaid_amount,allocationsis required and each of its blocks is positive, and the total of the third party's payments on the invoice does not exceedprepaid_amount.
What the payer changes:
- Every payment writes one
212event and only one, carrying its own amount, whether it comes from the buyer or from the third party. - On an invoice with an amount still due (
payable_amountgreater than zero), the third party's payment settles a share thatpayable_amountalready deducts: it writes its212, but never moves the invoice to212Collected and is not counted inpaid_total. Only the buyer's payments settlepayable_amount. - On a fully prepaid invoice (
payable_amountat0), the collection is recorded once, whoever the payer, and for exactlyprepaid_amount: a payment of another amount is refused, and so is a second payment. To correct it, modify or delete the existing payment.
View an invoice's payments
GET /api/v1/invoices/{invoice_id}/payments returns all of the invoice's payments, most recent first (payment_date, then created_at). The list is not paginated: no meta, no page parameter, no filter. The read scope is enough.
curl https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 88,
"amount": 770.0,
"payment_date": "2025-01-20",
"payment_means_code": "59",
"currency_code": "EUR",
"reference": "PM0012ABCDEF",
"note": null,
"payer_role": "buyer",
"allocations": [],
"created_at": "2025-01-20T04:02:11Z"
},
{
"id": 87,
"amount": 1000.0,
"payment_date": "2025-01-10",
"payment_means_code": "30",
"currency_code": "EUR",
"reference": "VIR-2025-0110",
"note": null,
"payer_role": "buyer",
"allocations": [],
"created_at": "2025-01-10T09:15:00Z"
}
]
}
A single payment can be read at GET /api/v1/invoices/{invoice_id}/payments/{id}, with the same fields.
Not every listed payment comes from you
The list also contains the payments Scribee recorded itself: a collected GoCardless direct debit, a bank reconciliation confirmed in the interface. No field in the response indicates the source. Two hints only: a GoCardless direct debit carries payment_means_code 59 and a reference equal to the GoCardless payment identifier; a payment from bank reconciliation carries in reference the reference of its bank operation's accounting entry, BQ-YYYY-MM-NNNN: BQ, the entry's year and month - normally the operation's; a later correction of the operation's date does not change them -, then the operation's Scribee identifier - the one bank_operation_id returns - on at least four digits, for example BQ-2026-07-0144 (Bank accounting entries).
These payments used to carry a different reference. Those still carrying the value assigned when they were created have been moved to this format; their id has not changed. If you kept the former reference to find one of these payments, match it on its id from now on.
So do not assume every listed payment is deletable, or fully editable, and keep the identifiers returned by your own POST calls if you need to tell your entries apart from the others.
Correct a payment
This call updates the payment in place and immediately recalculates the invoice's collection status as well as, where applicable, the e-reporting declaration - the VAT-rate breakdown is recomputed, and the payment is moved to another period's declaration if payment_date changes. If the invoice has left the scope of the payments declaration since the payment was carried into it - buyer now identified by a SIREN and established in the French VAT territory, lines now all goods, VAT switched to reverse charge or to the debit option -, the correction removes the line from the declaration; if that declaration is closed, the removal is deferred (Declare payments). Exception: a payment from a confirmed bank reconciliation only accepts note, and that change recalculates nothing (see below).
PATCH /api/v1/invoices/{invoice_id}/payments/{id} accepts the same fields as creation; only the fields sent change. The response is a 200 carrying the updated payment. The token must carry the read write scopes.
payer_role cannot be changed: sending it back with the recorded value goes through, any other value is refused with 422 - delete the payment and record it again. allocations, when sent, replaces the payment's whole set of blocks; when omitted, the existing blocks are kept.
Changing amount on a payment that carries blocks requires sending allocations again: the kept blocks would no longer add up to the amount, and the request is refused with 422. When the split changes, the amount difference is no longer enough to describe it: Scribee writes a 212 event that cancels the previous one in full - the payment's amount before the change, with the opposite sign, with its blocks, if any, with the opposite sign -, then a 212 that declares the payment with its new split.
A payment from a GoCardless direct debit is only partly editable: amount, payment_date and payment_means_code are refused with a 422 (see below), and only reference and note stay editable. The refusal fires on the key being present in the payload, not on a value that differs: sending back the amount already recorded is refused too.
A payment from a confirmed bank reconciliation only accepts note: any other key is refused with a 422 dependent_records (see below), even when sent with the value already recorded, and the request is then refused whole - the note it carried is not applied either. A PATCH carrying only note changes the note and nothing else: the amount, the date, the reference, the invoice and the link to the bank operation stay as they are, the invoice's collection status does not move, no lifecycle event is recorded - so no invoice.lifecycle_event.created is emitted -, and neither the e-reporting declaration nor the operation's accounting entry is recalculated. This PATCH carries no version check: two concurrent note changes apply one after the other, and the last one wins.
curl -X PATCH https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments/YOUR_PAYMENT_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "payment": { "amount": 900.0 } }'
{
"data": {
"id": 87,
"amount": 900.0,
"payment_date": "2025-01-10",
"payment_means_code": "30",
"currency_code": "EUR",
"reference": "VIR-2025-0110",
"note": null,
"payer_role": "buyer",
"allocations": [],
"created_at": "2025-01-10T09:15:00Z"
}
}
Delete a payment
This call deletes the payment, removes the corresponding line from the e-reporting declaration draft, and, if the total falls back below the amount due for payment, moves the invoice back from 212 Collected to 211 Payment sent.
The deletion writes a 212 event for the payment's amount, with the opposite sign; if the payment carried blocks, that event restates them, signs reversed.
DELETE /api/v1/invoices/{invoice_id}/payments/{id} responds 204 with no body. The token must carry read, plus destroy or write.
Two refusals are possible, 422: a payment from a GoCardless direct debit is never deletable, nor is a payment from a confirmed bank reconciliation. These cases are detailed below.
curl -X DELETE https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/payments/YOUR_PAYMENT_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
What happens next
- Every status transition - the move to
212Collected as well as the reversion to211Payment sent - records a lifecycle event on the invoice and triggers theinvoice.lifecycle_event.createdevent toward your notification endpoints (see Webhooks). The status details are in The invoice lifecycle. - For a B2C or international B2B sales invoice whose VAT is due on collection, and whose issuing company is subject to the payment reporting obligation, the payment is broken down by VAT rate, in euros, in its period's e-reporting declaration, created if necessary. Purchase invoices, sales carrying any other tax point code, sales whose VAT breakdowns are all in category
GorO, and companies whose VAT regime does not carry that obligation give rise to no payment declaration: recording there only serves to track settlement. The exclusions specific to the sale - a buyer identified by a SIREN or a SIRET and established in the French VAT territory, whose212Collected status declares the collection, a seller established outside the VAT territory, a wholly reverse-charged sale, a delivery of goods - are detailed in Declare payments. - Nothing is transmitted to the DGFiP at the time of the call: the payment joins the draft of its period's declaration, whose content is described in Declare payments.
Errors and edge cases
401 Unauthorized
Missing, expired, or invalid token. Empty body; request a new token at /oauth/token (Authentication).
403 Forbidden: insufficient scope
Reads on this page require read. On this resource, read is also required of writes, on top of their own scope: POST and PATCH require read and write; DELETE requires read and (destroy or write). Request read write to cover every call on this page:
{
"error": "forbidden",
"message": "Vous n'êtes pas autorisé à effectuer cette action"
}
Request a new token with scope="read write" (Authentication).
404 Not Found
Three causes: the invoice or the payment does not exist; the invoice belongs to a workspace outside your application's scope, or to one whose allowed-IP list rejects your call; the invoice is a sales invoice and the company's offer does not cover sales invoicing.
{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}
A 404 on an identifier you know to be correct comes from the last two causes, not from a typo.
422: incompatible invoice status
A POST on an invoice that is not in one of the statuses listed above is refused:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Les paiements ne peuvent être enregistrés que sur des factures émises."]
}
}
Deposit the invoice first (Issue a sales invoice), then record the payment. PATCH and DELETE do not re-check the status: a payment already recorded stays editable if the invoice changes status afterwards.
422: GoCardless payment
A payment created by the collection of a GoCardless direct debit is never deletable through the API:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Les paiements GoCardless ne peuvent pas être supprimés manuellement. Contactez le support si une correction est nécessaire."]
}
}
PATCH is blocked too, but only in part. As soon as the payload carries amount, payment_date or payment_means_code - the key being present is enough, the value sent is never compared with the recorded one:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Le montant, la date et le moyen de paiement d'un paiement GoCardless ne peuvent pas être modifiés. Seules la référence et la note peuvent être mises à jour."]
}
}
A PATCH carrying only reference and note goes through: they are local annotations, with no GoCardless counterpart.
422: payment from a bank reconciliation
A payment created by confirming a bank reconciliation cannot be deleted through this endpoint, and a PATCH carrying any key other than note is refused whole, even when that key carries the value already recorded. The response carries the code dependent_records and, in details.bank_operation_id, the Scribee identifier of the reconciled bank operation:
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Ce paiement provient d'une opération bancaire rapprochée et ne peut pas être supprimé. Contactez le support si une correction est nécessaire.",
"details": {
"base": ["Ce paiement provient d'une opération bancaire rapprochée et ne peut pas être supprimé. Contactez le support si une correction est nécessaire."],
"bank_operation_id": [30144]
}
}
The PATCH refusal has the same shape, with its own message:
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Le montant, la date, le moyen de paiement et la référence d'un paiement issu d'une opération bancaire rapprochée ne peuvent pas être modifiés. Seule la note peut être mise à jour.",
"details": {
"base": ["Le montant, la date, le moyen de paiement et la référence d'un paiement issu d'une opération bancaire rapprochée ne peuvent pas être modifiés. Seule la note peut être mise à jour."],
"bank_operation_id": [30144]
}
}
Nothing is written, and sending the same call again will be refused again. To undo this payment, undo the confirmed allocation that created it, and only that one:
- Find it with
GET /api/v1/bank_operations/{bank_operation_id}/allocationsfiltered bystatus=confirmedand byinvoice_document_idequal to this invoice's identifier (Listing allocations). Itsidis the allocation's identifier. - Undo it with
DELETE /api/v1/bank_operations/{bank_operation_id}/reconciliationwithallocation_idsnarrowed to thatid(Undoing a confirmation): the allocation becomesunconfirmedand its payment is removed as one act.
Do not omit allocation_ids. Without the key, every confirmed allocation on the operation is undone: on an operation split across several invoices, the other invoices' payments would be removed along with this one.
422: invalid fields
amount must be strictly positive, payment_date is required, and payment_means_code, if provided, must belong to the list of accepted codes. The messages arrive in details.base.
422: payer or split refused
Each breached rule of the split by rate adds its message to details.base. On a fully prepaid invoice whose collection is already recorded, a second POST is refused like this:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Cette facture était déjà payée à son émission et son encaissement est déjà enregistré : corrigez ou supprimez ce paiement au lieu d'en ajouter un autre."]
}
}
A PATCH that changes payer_role is refused like this:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Le payeur d'un paiement enregistré ne peut pas être modifié. Supprimez le paiement puis enregistrez-le à nouveau."]
}
}
Related pages
- The invoice lifecycle - the details of statuses
211and212 - Receive supplier invoices - track settlement of your purchase invoices
- API reference: list an invoice's payments
- API reference: record a payment