NutraSoft food manufacturing ERP software
Esc
  • GuideGetting startedThree steps to your first call and your first order, then the two shapes every response takes.
  • GuideAuthentication and keysBearer keys, test and live environments, rotation without an outage, and what each scope grants.
  • GuideTest modeA second NutraSoft account of your own, seeded with realistic data, that you can safely break.
  • PageErrorsEvery error code the API returns, what it means, what to do about it and whether to retry.
  • PageConventionsThe rules every operation follows: money, time, paging, sorting, nulls, PATCH and identifiers.
Menu

Lots and incremental sync

Base units, the five quality statuses and which lots you can sell, and how to poll for changes honestly.
Updated

In short

  • A lot is sellable when qc_status is AVAILABLE, or RELEASED with a qc_release_expires_at that is null or still ahead.
  • A lot whose lot_number is null is the generic lot: real stock, not a data error.
  • A retired lot is archived and stays readable. Pass archived=false for live stock.
  • updated_since is strictly greater than, follows the parent row only and never reports deletions. Overlap each poll by a minute.

A lot is a quantity of one product, in one location, that arrived or was made together. Everything about stock on this API is expressed in lots.

Quantities are base units

quantity_base is in the product's base unit, on every operation, in both directions. To show a lot in the product's inventory unit, divide by both factors on the lot: quantity_base / (uom_qty * uom_base_conversion). See Units and quantities.

Quality status

qc_status has five values: AVAILABLE, RELEASED, ON_HOLD, QUARANTINE and REJECTED. Two of them are sellable: AVAILABLE and RELEASED.

Under positive release, production output starts ON_HOLD and moves to RELEASED, never back to AVAILABLE, so filtering on AVAILABLE alone misses every positively released finished good.
StatusMeans
AVAILABLEIn stock, and no release decision was needed. Sellable.
RELEASEDSigned off by quality. Sellable until qc_release_expires_at, if set.
ON_HOLDMade or received, not yet released. Yours, not sellable.
QUARANTINEHeld pending investigation. Yours, not sellable.
REJECTEDFailed. Not sellable.

Release expiry

A release can carry a retest date, published as qc_release_expires_at. Only RELEASED lots have one, and it is null when the release is open-ended. Once it has passed, NutraSoft will not ship the lot or use it in production until quality retests and releases it again. The sellable rule, in full: qc_status is AVAILABLE, or it is RELEASED and qc_release_expires_at is null or still in the future.

The generic lot

A lot whose lot_number is null is the generic lot: the unnumbered bucket created with a product to hold stock that was never lotted. It is a real row with a real quantity. Do not filter it out, and do not treat a null lot number as a data error.

Locations

A lot's location is location_id: a warehouse id or a location inside one. When you create a lot, the warehouse id you send is location_id. To list the stock of a whole warehouse, filter GET /product-lots with warehouse_id.

Retiring a lot

A lot that is used up, spoiled or written off is archived: archived becomes true and the row stays readable with its history. Without an archived filter, the list returns both, so pass archived=false for live stock and archived=true to audit what was retired. A lot can also be deleted outright, when someone removes it from a product in NutraSoft; a deleted lot simply stops appearing.

Incremental sync with updated_since

Seven resources accept updated_since: products, product lots, sales orders, invoices, purchase orders, customers and suppliers. Each publishes created_at and updated_at and can be sorted by them. Four rules are not guessable, so read them before you build a sync:

  • It is strictly greater than. A row whose updated_at equals the value you pass is not returned again.
  • It tracks the parent row only. Editing a sales order's lines does not necessarily move the order's updated_at. If you care about lines, re-fetch the documents you hold on a slower cycle.
  • Timestamps are written when the row is saved, not when its transaction commits. A slow transaction can land a row whose updated_at is slightly earlier than rows you have already seen. Overlap each poll by a minute and de-duplicate by id.
  • Deletions are invisible. Archiving a lot moves its updated_at, so an archived lot shows up in the feed. A deleted row, a lot included, simply stops appearing, and polling will never tell you about it.

A sync that holds up

  1. On the first run, page through the whole resource with sort=updated_at:asc.
  2. Record the highest updated_at you saw.
  3. Poll with updated_since set to that value minus a minute, sorted the same way, and upsert by id.
  4. On a slow cycle, nightly or weekly, re-list the resource in full to catch deletions.

Offset paging stops at an offset of 10,000. For a large first run, sort by updated_at and move updated_since forward to the last updated_at of each page, minus the same overlap, instead of paging deeper.

Polling tells you what changed since you last asked, but not that it changed. Webhooks push each change to you within a minute, including line edits and deletions, which updated_since cannot report. Most integrations run both: webhooks to react, a periodic sweep to reconcile.