Writes & batches
Create GP records via eConnect — idempotency, the write whitelist, and the unposted-batch reality.
Writes are POST /v1/{object} calls. Every write:
- Requires an
Idempotency-Keyheader. Retrying with the same key + body returns the stored201with headerIdempotent-Replay: trueand never re-executes. - Is disabled by default per connection per object. Enable it in the dashboard
(Connections → Write access) or with
PATCH /v1/connections/:id{ "writeWhitelist": ["customers", …] }. Otherwise403 WRITE_NOT_ENABLED. Requires thewritescope; sandbox connections are read-only. - Requires
Authorization: Bearer lb_…,X-Connection-Id, andX-Company. - Goes to GP only through eConnect, inside a transaction. A GP validation failure →
422 GP_VALIDATIONwithgpErrors[{ number, text, node, hint? }]; the whole document rolls back. - Sends and returns money as decimal strings (never floats).
Unposted-batch reality. Transaction writes (journal entries, AP/AR invoices, cash receipts, sales orders) land in an unposted batch — ERPSpan auto-creates a per-day batch (
LBAPI-YYYYMMDD) and a human posts it in GP. Cash receipts additionally land unapplied. Master upserts (customers, vendors) are live on commit (no batch). Purchase orders land asnew(no batch). Numbers that eConnect auto-assigns (journal entry, sales order, purchase order) are provisional until the batch is posted.
Master upserts (live on commit)
POST /v1/customers
{ id (req, ≤15), name (req), classId?, address?{ code, line1, line2, city, state, postalCode, country, contactPerson }, phone?, paymentTermsId?, salespersonId?, currencyId?, taxScheduleId?, inactive?, hold? } → 201 { id }. Bad classId → 422 (eConnect 305).
POST /v1/vendors
{ id (req, ≤15), name (req), checkName?, classId?, address?{ code, contactPerson, line1-3, city, state, postalCode, country }, phone?, paymentTermsId?, taxId?, status? (active|inactive|temporary) } → 201 { id }.
Transaction writes (unposted batch)
POST /v1/journal-entries
{ date (req, YYYY-MM-DD), reference?, lines (2..500) [{ accountNumber (req), debit?, credit?, description? }] }. Each line has exactly one of debit/credit (positive, ≤2 dp); Σdebit == Σcredit. → 201 { journalEntryNumber, batchId, status: "unposted" }. Unknown account → 422 (7).
POST /v1/ap-invoices
{ vendorId (req), vendorDocumentNumber (req), amount (req), date (req), dueDate?, purchasesAmount? (default = amount), description?, paymentTermsId? } → 201 { vendorDocumentNumber, batchId, status: "unposted" }. Unknown vendor → 422 (330).
POST /v1/ar-invoices
{ customerId (req), documentNumber (req), amount (req), salesAmount? (default = amount), date (req), description? } → 201 { documentNumber, batchId, status: "unposted" }. Unknown customer → 422 (4628).
POST /v1/cash-receipts
{ customerId (req), documentNumber (req), amount (req), date (req), checkNumber?, paymentType? (cash|check|creditCard) } → 201 { documentNumber, batchId, status: "unposted" } — lands unapplied. Unknown customer → 422 (4628).
POST /v1/sales-orders
{ type (order|invoice), docTypeId (req), customerId (req), date (req), customerPONumber?, siteId?, lines (≥1) [{ itemId (req), description?, quantity (req), unitPrice (req), extendedPrice? (default = quantity × unitPrice), siteId? }], subtotal? (default = Σ line extended), total? (default = subtotal) } → 201 { salesOrderNumber, type, batchId, status: "unposted" }. Unknown item → 422 (-101).
POST /v1/purchase-orders (a document, no batch)
{ vendorId (req), date (req), buyerId?, siteId?, lines (≥1) [{ itemId (req), quantity (req), unitCost (req), siteId?, promisedDate? }] } → 201 { purchaseOrderNumber, status: "new" }. Unknown item → 422 (-101); unknown vendor → 422 (330).
Reading the record back
Fetch the created record with the object's read endpoint — for example
GET /v1/sales-orders/2:<salesOrderNumber> and …/lines, or
GET /v1/purchase-orders/<purchaseOrderNumber> and …/lines.