REST API for Accounting Data
Because every REDA Financials document is a Salesforce custom object, the standard REST API is your integration front door: create invoices from your CRM or billing system, query balances into a data warehouse, post documents from middleware — all with the endpoints your team already knows.
The canonical pattern: create draft, then post
Insert the header as a Draft
POST the document (e.g. refin__Sales_Invoice__c) with entity, customer, dates — status omitted (defaults to Draft) or explicitly 'Draft'. The triggers behave exactly as in the UI: document number assigned from the entity's sequence, ledger and period derived, due date from payment terms.
Insert the lines
POST the line records (refin__Sales_Invoice_Line__c) referencing the header — products, quantities, prices, tax profiles, exactly the fields the form asks for. (The composite API bundles steps 1–2 into one call neatly.)
PATCH the status to Posted
Update refin__Status__c to 'Posted'. This single field update is the posting action: the engine writes the ledger entries, calculates tax and withholding, stamps exchange rates — and enforces every gate (balanced, open period, approval, active accounts). A refused posting comes back as a standard DML error with the same verbatim message a user would see.
The endpoints, at a glance
Nothing here is REDA-specific — these are the standard Salesforce REST endpoints, shown against the refin__ objects (the package is built at API v65.0; any current version works):
| Task | Method & endpoint |
|---|---|
| Create a document header | POST /services/data/v65.0/sobjects/refin__Sales_Invoice__c |
| Read one record | GET /services/data/v65.0/sobjects/refin__Sales_Invoice__c/{id} |
| Update (including posting) | PATCH /services/data/v65.0/sobjects/refin__Sales_Invoice__c/{id} |
| Query | GET /services/data/v65.0/query?q=<SOQL> |
| Header + lines in one round-trip | POST /services/data/v65.0/composite (or composite/tree) |
| Discover fields & picklists | GET /services/data/v65.0/sobjects/refin__Sales_Invoice__c/describe |
Posting a draft, on the wire
The posting call is deliberately anticlimactic — one field:
PATCH /services/data/v65.0/sobjects/refin__Sales_Invoice__c/a0X8d000001AbCdEAV
{
"refin__Status__c": "Posted"
}
Success is an HTTP 204 No Content — and a fully posted document behind it. A refusal comes back as the standard error array carrying the package's human-readable message; for example, an integration user without a REDA licence seat sees:
[
{
"message": "You must be a licensed REDA Financials user to create, edit, or delete Accounting Transactions. Contact your administrator.",
"errorCode": "FIELD_CUSTOM_VALIDATION_EXCEPTION",
"fields": []
}
]
Reading back what you created is plain SOQL:
GET /services/data/v65.0/query
?q=SELECT Id, Name, refin__Status__c
FROM refin__Sales_Invoice__c
WHERE refin__Status__c = 'Draft'
For the insert bodies themselves, pull the field list from the describe endpoint or Object Manager rather than this article — your org's field sets and required fields are the truth, and the extra dimension fields your admin added are part of it.
What the platform fills in for you
| On… | The triggers do… | Integration consequence |
|---|---|---|
| Insert with no status | Status defaults to 'Draft' | You may omit refin__Status__c on creation |
| Insert | Document number assigned from the entity's sequence (type-aware — invoices and memos number separately); pre-setting Name opts into manual numbering, with uniqueness validated either way | Don't generate your own numbers unless you mean to own them |
| Insert | Entity defaults fill: ledger, accounting period, due date from the vendor's payment term | Send the business fields; skip the derivable ones |
| Changing Entity or Invoice Type on a Draft | The document re-sequences its number | Expect a new number if your middleware "moves" a draft between entities |
| Draft → Posted | The full engine: ledger entries, tax + WHT calculation, exchange-rate stamping, every guard | Posting via API is byte-for-byte the UI's posting |
Reading data
- SOQL through the query endpoint — open invoices, unapplied receipts, aging fields (they're formula fields on the records), whatever your dashboard needs.
- The ledger itself —
refin__Accounting_Transaction__cis the posting truth; sum debits and credits by GL account and you've rebuilt any balance externally. - Rollups are fields — an invoice's balance due, a receipt's unapplied amount, a budget line's committed figure: all queryable directly, no computation on your side.
Rules of the road
- No bypass exists: posted documents are locked to API edits exactly as to users; corrections go through unpost/void flows, programmatically the same status transitions.
- Applications are records too — creating a
refin__Cash_Receipt_Application__capplies a receipt (all the matching guards run); deleting it un-applies. - Respect the closure dates — writes into closed books are refused; your integration should date documents in open periods like any accountant.
- Idempotency is on you — the natural key is your source system's reference (put it in the Reference field and check before insert; the vendor-reference duplicate validation helps on the AP side).
Common mistakes to avoid
| Mistake | Why it hurts | Do this instead |
|---|---|---|
| Inserting documents with Status 'Posted' in a single call | Posting is engineered around the Draft → Posted transition; a direct insert-as-Posted is outside the supported pattern and its behavior isn't guaranteed | Insert Draft, then PATCH to Posted — composite makes it one round-trip anyway |
| Generating your own document numbers casually | Pre-setting Name switches that document to manual numbering — now uniqueness is your job, per entity | Let the sequences number documents; reserve manual numbers for deliberate migration scenarios |
| Running the integration as a borrowed admin login | Audit trails blur, password rotations break the feed, and permissions are far wider than the job | A dedicated integration user with a licence seat and the narrowest packaged permission set that does the work |
| Retrying failed posts blindly | The message told you why it failed — a closed period retries into the same wall forever | Parse the error, fix the data (or route to a human), then retry |
| Skipping the Reference field | You lose your idempotency key and bank reconciliation's best matching signal | Always carry the source system's real identifier in Reference |
Where to go next
- Bulk API — the same pattern at migration scale.
- API Overview — the whole landscape.
- Opening Balances & Data Migration — the classic REST/Bulk project.
Frequently asked questions
Use the two-step (insert Draft, then update to Posted) — it's the pattern the engine is built around, it gives you a clean error boundary between "bad data" and "posting refused", and it mirrors the UI's save-then-post. Composite calls make it one round-trip anyway.
They're standard DML errors carrying the package's verbatim validation messages ("Transaction Date ... is on or before the GL book closure date...", "This document requires approval before posting."). Log them, surface them, fix the data — the messages are written for humans.
The package targets API v65+; use a current version and the refin__ objects behave like any custom objects at that version.
Yes, if it creates or posts accounting documents — the licence check runs on API writes exactly as in the UI, and an unseated user is refused with the verbatim message shown above. Give the integration user a seat and the appropriate permission set, same as a human.
No — the package ships zero custom REST resources by design. The standard sObject, query and composite endpoints against refin__ objects are the complete REST surface, which means every Salesforce REST client, ETL tool and middleware connector already speaks it.
Was this article helpful?