11. Invoice lifecycle
Observed — rental-account balance journal / invoice preview & payment (createRentalAccountInvoice → freeze → payment → link → lock).
Why
Invoice a rent term (and charges), record the payment, attach it to the invoice, then lock the document once paid.
Prerequisites
- Rental account (
accountId/rentalAccountId) - Invoice template (
accountingInvoiceConfigId) - Accounting operations for line items (
operationId) - VAT codes:
vatCodeIdandvatExemptionCodeIdon eachinvoiceItem(optional in the schema today, expected to become required — resolve them vialistVatCodes/listVatExemptionCodes) - Amounts in euro cents (e.g.
60000= €600.00)
Resolve VAT codes
query listVatCodes {
listVatCodes { id code label }
}
query listVatExemptionCodes($vatCodeId: UUID) {
listVatExemptionCodes(vatCodeId: $vatCodeId) { id code legalReason }
}
11.1 Create the invoice (createRentalAccountInvoice)
- GraphQL
- Node.js
- curl
mutation createRentalAccountInvoice($accountId: UUID!, $rentalAccountInvoice: RentalAccountInvoiceInput!) {
createRentalAccountInvoice(accountId: $accountId, rentalAccountInvoice: $rentalAccountInvoice) {
id
invoices {
id
insertedAt
}
}
}
{
"accountId": "00000000-0000-4000-8000-000000000001",
"rentalAccountInvoice": {
"accountingInvoiceConfigId": "00000000-0000-4000-8000-000000000002",
"dueDate": "2026-08-03",
"term": "2026-08",
"nature": "standard",
"invoiceItems": [
{
"amount": 60000,
"operationId": "00000000-0000-4000-8000-000000000003",
"vatCodeId": "00000000-0000-4000-8000-000000000201",
"vatExemptionCodeId": "00000000-0000-4000-8000-000000000202",
"vatRate": 0,
"vatMention": "not_applicable",
"vatSpecific": "accounting"
},
{
"amount": 5000,
"operationId": "00000000-0000-4000-8000-000000000004",
"vatCodeId": "00000000-0000-4000-8000-000000000203",
"vatExemptionCodeId": "00000000-0000-4000-8000-000000000204",
"vatRate": 1000,
"vatMention": "intermediate",
"vatSpecific": "accounting"
}
],
"referencedCreditNoteId": null,
"referencedInvoiceId": null,
"referencedDocumentIdentifier": null,
"type": "invoice"
}
}
import { ublo } from '../ubloClient.mjs';
const data = await ublo(
`mutation createRentalAccountInvoice($accountId: UUID!, $rentalAccountInvoice: RentalAccountInvoiceInput!) {
createRentalAccountInvoice(accountId: $accountId, rentalAccountInvoice: $rentalAccountInvoice) {
id
invoices { id }
}
}`,
{
accountId: '00000000-0000-4000-8000-000000000001',
rentalAccountInvoice: {
accountingInvoiceConfigId: '00000000-0000-4000-8000-000000000002',
dueDate: '2026-08-03',
term: '2026-08',
nature: 'standard',
invoiceItems: [
{
amount: 60000,
operationId: '00000000-0000-4000-8000-000000000003',
vatCodeId: '00000000-0000-4000-8000-000000000201',
vatExemptionCodeId: '00000000-0000-4000-8000-000000000202',
vatRate: 0,
vatMention: 'not_applicable',
vatSpecific: 'accounting',
},
{
amount: 5000,
operationId: '00000000-0000-4000-8000-000000000004',
vatCodeId: '00000000-0000-4000-8000-000000000203',
vatExemptionCodeId: '00000000-0000-4000-8000-000000000204',
vatRate: 1000,
vatMention: 'intermediate',
vatSpecific: 'accounting',
},
],
type: 'invoice',
},
},
);
const invoiceId = data.createRentalAccountInvoice.invoices[0].id;
- Bearer
- Cookie session
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H "Authorization: Bearer $UBLO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d @- <<'EOF'
{
"query": "mutation createRentalAccountInvoice($accountId: UUID!, $rentalAccountInvoice: RentalAccountInvoiceInput!) { createRentalAccountInvoice(accountId: $accountId, rentalAccountInvoice: $rentalAccountInvoice) { id invoices { id } } }",
"variables": {
"accountId": "00000000-0000-4000-8000-000000000001",
"rentalAccountInvoice": {
"accountingInvoiceConfigId": "00000000-0000-4000-8000-000000000002",
"dueDate": "2026-08-03",
"term": "2026-08",
"nature": "standard",
"invoiceItems": [
{ "amount": 60000, "operationId": "00000000-0000-4000-8000-000000000003", "vatCodeId": "00000000-0000-4000-8000-000000000201", "vatExemptionCodeId": "00000000-0000-4000-8000-000000000202", "vatRate": 0, "vatMention": "not_applicable", "vatSpecific": "accounting" },
{ "amount": 5000, "operationId": "00000000-0000-4000-8000-000000000004", "vatCodeId": "00000000-0000-4000-8000-000000000203", "vatExemptionCodeId": "00000000-0000-4000-8000-000000000204", "vatRate": 1000, "vatMention": "intermediate", "vatSpecific": "accounting" }
],
"type": "invoice"
}
}
}
EOF
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H 'Content-Type: application/json' \
-b cookies.txt \
-d @- <<'EOF'
{
"query": "mutation createRentalAccountInvoice($accountId: UUID!, $rentalAccountInvoice: RentalAccountInvoiceInput!) { createRentalAccountInvoice(accountId: $accountId, rentalAccountInvoice: $rentalAccountInvoice) { id invoices { id } } }",
"variables": {
"accountId": "00000000-0000-4000-8000-000000000001",
"rentalAccountInvoice": {
"accountingInvoiceConfigId": "00000000-0000-4000-8000-000000000002",
"dueDate": "2026-08-03",
"term": "2026-08",
"nature": "standard",
"invoiceItems": [
{ "amount": 60000, "operationId": "00000000-0000-4000-8000-000000000003", "vatCodeId": "00000000-0000-4000-8000-000000000201", "vatExemptionCodeId": "00000000-0000-4000-8000-000000000202", "vatRate": 0, "vatMention": "not_applicable", "vatSpecific": "accounting" },
{ "amount": 5000, "operationId": "00000000-0000-4000-8000-000000000004", "vatCodeId": "00000000-0000-4000-8000-000000000203", "vatExemptionCodeId": "00000000-0000-4000-8000-000000000204", "vatRate": 1000, "vatMention": "intermediate", "vatSpecific": "accounting" }
],
"type": "invoice"
}
}
}
EOF
11.2 Freeze the invoice (freezeRentalAccountInvoiceV2)
note
In the admin UI, the GraphQL document may use a different operation name.
mutation freezeRentalAccountInvoiceV2($id: UUID!, $accountId: UUID!) {
freezeRentalAccountInvoiceV2(id: $id, accountId: $accountId) {
id
}
}
{
"id": "00000000-0000-4000-8000-000000000005",
"accountId": "00000000-0000-4000-8000-000000000001"
}
- Bearer
- Cookie session
# payload.json = query + variables above
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H "Authorization: Bearer $UBLO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d @payload.json
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H 'Content-Type: application/json' \
-b cookies.txt \
-d @payload.json
11.3 Create the payment transaction (createTransaction)
Observed amount: 65500 (€650.00 incl. tax for both lines).
- GraphQL
- Node.js
- curl
mutation createTransaction($accountId: UUID!, $rentalAccountTransaction: RentalAccountTransactionInput!) {
createTransaction(accountId: $accountId, rentalAccountTransaction: $rentalAccountTransaction) {
id
transactions {
id
amount
remainingAmount
state
}
}
}
{
"accountId": "00000000-0000-4000-8000-000000000001",
"rentalAccountTransaction": {
"sourceAccountId": "00000000-0000-4000-8000-000000000006",
"operationId": "00000000-0000-4000-8000-000000000007",
"paymentMethod": "bank_transfer",
"amount": 65500,
"date": "2026-08-03"
}
}
const { createTransaction: account } = await ublo(
`mutation createTransaction($accountId: UUID!, $rentalAccountTransaction: RentalAccountTransactionInput!) {
createTransaction(accountId: $accountId, rentalAccountTransaction: $rentalAccountTransaction) {
id
transactions { id amount remainingAmount state }
}
}`,
{
accountId: '00000000-0000-4000-8000-000000000001',
rentalAccountTransaction: {
sourceAccountId: '00000000-0000-4000-8000-000000000006',
operationId: '00000000-0000-4000-8000-000000000007',
paymentMethod: 'bank_transfer',
amount: 65500,
date: '2026-08-03',
},
},
);
const transactionId = account.transactions.at(-1).id;
- Bearer
- Cookie session
# payload.json = query + variables from the GraphQL tab
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H "Authorization: Bearer $UBLO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d @payload.json
curl -sS -X POST "$UBLO_GRAPHQL_URL" \
-H 'Content-Type: application/json' \
-b cookies.txt \
-d @payload.json
11.4 Link the payment to the invoice (updateInvoiceTransactions)
note
In the admin UI, the GraphQL document may use a different operation name.
mutation updateInvoiceTransactions(
$accountId: UUID!
$invoiceId: UUID!
$content: RentalAccountInvoiceTransactionsInput!
) {
updateInvoiceTransactions(accountId: $accountId, invoiceId: $invoiceId, content: $content) {
id
}
}
{
"accountId": "00000000-0000-4000-8000-000000000001",
"invoiceId": "00000000-0000-4000-8000-000000000005",
"content": {
"invoiceTransactions": [
{
"transactionId": "00000000-0000-4000-8000-000000000008",
"amount": 65500
}
]
}
}
11.5 Lock the paid invoice (lockPaidInvoice)
mutation lockPaidInvoice($id: UUID!, $accountId: UUID!) {
lockPaidInvoice(accountId: $accountId, id: $id) {
id
}
}
{
"id": "00000000-0000-4000-8000-000000000005",
"accountId": "00000000-0000-4000-8000-000000000001"
}
11.6 Optional — Unlock (unlockInvoice)
mutation unlockInvoice($id: UUID!, $accountId: UUID!) {
unlockInvoice(accountId: $accountId, id: $id) {
id
}
}