← Resources

NodinACC integration API v1

One API, organised by business event. Source apps (NodinPMS first, then POS, HR, Pay, and outside apps) send events; NodinACC acknowledges each one and posts them as balanced journals once per closed business day.

Endpoint: POST /functions/v1/nodinacc-ingest
Auth: request header x-nodinacc-key carrying the connector key (one per connector per company, created with create_acc_connector; only its hash is stored, the plain key is shown once).

Envelope

FieldTypeRequiredNotes
api_versionstringyes"1"
event_typeenumyessale, payment, refund_correction, deposit, supplier_bill, payroll_run, fee_commission
companystringyesNodinACC company id; must match the connector
source_appstringyese.g. nodinpms; must match the connector
object_typestringyese.g. reservation_sale, payment
object_idstringyesid in the source app
versionint >= 1yesincrement when the source changes the object
unique_keystringyescompany:source_app:object_type:object_id:version, must equal the parts
occurred_atRFC3339yes
business_dateYYYY-MM-DDnodefaults to the UTC date of occurred_at
currencyISO 4217yes
payloadobjectyessee below
reversal_ofstringrefund onlyunique_key of the event being reversed
metadataobjectnofree-form

Payloads

event_typeRequired fields
salenet, tax, gross (gross = net + tax), lines[] of {code, amount, description?}
paymentrelated_object_id, payment_method, amount
refund_correctionamount, reason; envelope reversal_of
depositreservation_id, amount, status (received / applied / refunded), payment_method when received
supplier_billsupplier_id, invoice_number, invoice_date, net, tax, gross, lines[]
payroll_runperiod_start, period_end, gross, deductions, net (net = gross - deductions)
fee_commissioncounterparty, fee_type, amount

Line codes and the well-known codes (receivable, guest_ledger, output_vat, input_vat, payable, customer_deposit, salary_expense, salaries_payable, payroll_deductions, payment:<method>, fee:<type>) are mapped to ledger accounts per company in acc_account_mappings.

Acknowledgement

{ "entry_id": null, "status": "accepted", "error": null, "posted_at": null, "event_id": "..." }
statusHTTPMeaning
accepted202stored; posts with the business day
duplicate200same unique_key seen before; original acknowledgement returned
rejected401 / 422error.code and, for validation, error.fields[]

entry_id and posted_at are filled once the day is posted (post_business_day_events).

Error codes: unauthorized, invalid_envelope, unsupported_version, unsupported_event_type, unknown_company, validation_error, unknown_account_mapping, unbalanced_entry, invalid_reversal, period_closed, version_conflict.

Rules

Examples

Room sale:

{ "api_version": "1", "event_type": "sale", "company": "c1", "source_app": "nodinpms",
  "object_type": "reservation_sale", "object_id": "booking-1", "version": 1,
  "unique_key": "c1:nodinpms:reservation_sale:booking-1:1",
  "occurred_at": "2026-10-01T10:00:00+07:00", "currency": "THB",
  "payload": { "net": 1000, "tax": 70, "gross": 1070,
    "lines": [ { "code": "room_revenue", "amount": 1000, "description": "Deluxe, 1 night" } ] } }

Payment:

{ "api_version": "1", "event_type": "payment", "company": "c1", "source_app": "nodinpms",
  "object_type": "payment", "object_id": "payment-1", "version": 1,
  "unique_key": "c1:nodinpms:payment:payment-1:1",
  "occurred_at": "2026-10-01T10:05:00+07:00", "currency": "THB",
  "payload": { "related_object_id": "booking-1", "payment_method": "promptpay", "amount": 1070 } }

Refund as reversal:

{ "api_version": "1", "event_type": "refund_correction", "company": "c1", "source_app": "nodinpms",
  "object_type": "refund", "object_id": "refund-1", "version": 1,
  "unique_key": "c1:nodinpms:refund:refund-1:1", "reversal_of": "c1:nodinpms:payment:payment-1:1",
  "occurred_at": "2026-10-02T09:00:00+07:00", "currency": "THB",
  "payload": { "amount": 1070, "reason": "Cancelled within free window" } }