Migrating from v2
Version 3 of the Nabis Customer API reorganizes the API around how Nabis stores its data today. This guide covers everything that changes between v2 and v3, so you can update an existing v2 integration.
v3 is in active development. This guide will be updated as v3 approaches release. v2 keeps working while you migrate, but its data will stop being updated after v3 is released, so plan to move before then.
What's changing
- One set of endpoints for every organization. v2 had separate California and New York endpoints (for example
/v2/orderand/v2/ny/order). v3 has a single set of endpoints. Your organization is identified by your API key, so your own records (orders, line items, invoices and inventory) are always limited to your organization. - Shared records span every market. Retailers, warehouses and Nabis days off aren't specific to your organization,
and v3 returns them for every market. In v2, the California and New York endpoints each only returned their own
state's records. To get the same results as before, pass a
marketIdfilter. See Markets. - A new base URL. v3 is served from
https://customer-api.nabis.pro/v3. Authentication is unchanged: send your API key in thex-nabis-access-tokenheader. - Field names follow Nabis' data model. Many fields are renamed. Products are now items instead of SKUs (for
example
skuCodeis nowitem.code), and IDs that point to other records are named after them (for examplelicensedLocationId,warehouseIdanditemBatchId). - Enum values match Nabis' data model. Some values are spelled differently (for example
CANCELEDinstead ofCANCELLED) and some lists have new values. See Enum changes. - Retired fields are removed. In v2, fields that didn't apply were often returned as
null. In v3, fields that are no longer supported are removed from the response entirely. - Orders are split into orders, line items and invoices. v2 returned one row per line item with order, product and
financial details repeated on every row. In v3,
/v3/ordersreturns one record per order, line items come from/v3/line-items, and financial details come from/v3/invoices. - Fewer calculated fields. Fields that were simple math on other fields (for example
skuTotalPrice) are removed. This guide lists the formula for each one. - IDs don't change. Orders, line items, items, batches, item batches, retailers, licenses and organizations keep the same IDs in v3, so you can match v3 records to the ones you already have.
Endpoints
| v2 | v3 |
|---|---|
GET /v2/warehouses, GET /v2/ny/warehouses | GET /v3/warehouses |
GET /v2/nabis-days-off | GET /v3/nabis-days-off |
GET /v3/markets (new) | |
GET /v2/retailer, GET /v2/ny/retailer | GET /v3/retailers |
GET /v2/retailer/{id}, GET /v2/ny/retailer/{id} | GET /v3/retailers/{id} |
licenses[] on a single retailer | GET /v3/retailers/{id}/licenses |
previousCreditRating on a retailer | GET /v3/retailers/{id}/credit-history |
GET /v2/inventory, GET /v2/ny/inventory | GET /v3/inventory |
GET /v2/inventory/{skuCode}, GET /v2/ny/inventory/{itemCode} | GET /v3/inventory?itemCode={code} |
GET /v3/inventory/{id} (new, by item batch ID) | |
GET /v2/ny/inventory/aggregate | Removed. Add up warehouseCounts from GET /v3/inventory |
GET /v2/order, GET /v2/ny/order | GET /v3/orders and GET /v3/line-items |
GET /v2/order/{id}, GET /v2/ny/order/{id} | GET /v3/orders/{id} and GET /v3/line-items?orderId={id} |
GET /v3/line-items/{id} (new) | |
GET /v2/invoice, GET /v2/ny/invoice | GET /v3/invoices |
GET /v3/invoices/{id} (new) | |
GET /v2/inventory-history | Removed (already deprecated in v2) |
Pagination works the same way in every paginated v3 endpoint: page and limit are required, and responses use the
same data, page, totalCount, totalNumPages, nextPage and prevPage structure. A limit over 500 now
returns a 400 Bad Request instead of being capped. See Pagination.
Markets
Retailers, warehouses and Nabis days off now include a marketId, and their list endpoints accept a marketId filter.
Without the filter, records from every market are returned.
If your v2 integration called the California or New York endpoints for retailers, warehouses or days off, pass the
matching marketId in v3 to get the same records. For example, to only list California retailers:
curl "https://customer-api.nabis.pro/v3/retailers?page=0&limit=100&marketId=6f7eaf7a-805d-4b38-87d5-21e2d33074bc" \
-H 'x-nabis-access-token: <access-token>'
Use GET /v3/markets to look up market IDs. See Markets.
Warehouses
| v2 | v3 | Notes |
|---|---|---|
id | id | |
name | name | |
region | region | Now the US state, for example CA, instead of NORTHERN_CALIFORNIA or SOUTHERN_CALIFORNIA |
marketId | New |
Nabis days off
| v2 | v3 | Notes |
|---|---|---|
date | date | |
description | description | |
marketId | New. Each day off is listed once per market |
Retailers
GET /v3/retailers now returns the full retailer, including the fields v2 only returned from the single retailer
endpoint.
GET /v3/retailers also returns retailers from every market. v2's /v2/retailer only returned California retailers
and /v2/ny/retailer only returned New York retailers. Pass marketId to only return retailers in your market. See
Markets.
| v2 | v3 | Notes |
|---|---|---|
id | id | Other v3 endpoints call this licensedLocationId |
parentOrganizationId | organizationId | |
parentOrganizationName | organizationName | |
name, doingBusinessAs | name, doingBusinessAs | |
address1, address2, city, state, zip | same | |
sellerPermitLink, w9Link, taxIdentificationNumber | same | Now returned for New York retailers too |
isArchived | isArchived | |
isOnMarketplace | isOnMarketplace | Now specific to your organization: false when you've blocked the retailer or disabled the marketplace |
creditRating | creditClass | Same values |
previousCreditRating, previousCreditRatingDate | Moved to GET /v3/retailers/{id}/credit-history | |
licenses[] | Moved to GET /v3/retailers/{id}/licenses | |
licenses[].number | licenseNumber | On the licenses endpoint. Licenses also gain category, legalEntityName and isArchived |
territoryRegionNumber, territoryDescription | Retired | |
marketId | New |
Inventory
Each inventory record is still one item batch (one batch of one item), but its fields are now grouped into item and
batch objects. Archived item batches are included and marked with isArchived; use the isArchived filter to return
only active or only archived inventory.
| v2 | v3 | Notes |
|---|---|---|
skuBatchId | id | |
skuBatchLastUpdatedDate | updatedAt | Now a timestamp instead of a date |
skuBatchDaysAtNabis | daysAtNabis | |
deletedAt (NY) | isArchived | |
warehouseCounts | warehouseCounts | Unchanged |
skuId (NY) | item.id | |
skuCode | item.code | |
skuName | item.name | |
skuUnit | item.unit | |
skuPricePerUnit | item.pricePerUnit | |
skuCasePackSize, skuUnitsPerEach | item.quantityPerUnitOfMeasure | |
skuInventoryType, skuInventoryClass, skuInventoryCategory | item.inventoryType, item.inventoryClass, item.inventoryCategory | See Inventory Classifications |
skuStrainType | item.strainType | |
skuBrandName | item.subBrand | |
skuProductLine, skuContainerType, skuIsColdStorage | item.productLine, item.containerType, item.isColdStorage | |
skuIsSample | item.isSample | |
isMedical, availableOnMarketplace (NY) | item.isMedical, item.availableOnMarketplace | Now returned for every organization |
skuCannabinoidTypeA, skuCannabinoidTypeB | item.cannabinoidTypeA, item.cannabinoidTypeB | |
skuTHC, skuCBD, ... skuDelta9THC (16 fields) | item.cannabinoidDosageA, item.cannabinoidDosageB | See Cannabinoids |
batchCode | batch.code | |
batchManufacturingDate, batchExpirationDate | batch.manufacturingDate, batch.expirationDate | |
batchLicenseNumber | batch.licenseNumber | |
licenseNickname (NY) | batch.licenseNickname | |
batchThcPercentage, batchThcAPercentage, batchCbdPercentage, batchOtherCannabinoidPercentage | batch.thcPercentage, batch.thcAPercentage, batch.cbdPercentage, batch.otherCannabinoidPercentage | |
batchThcWeight, batchThcAWeight, batchCbdWeight, batchOtherCannabinoidWeight | batch.thcWeight, batch.thcAWeight, batch.cbdWeight, batch.otherCannabinoidWeight | |
skuThcPotencyPercentage | batch.calculatedThcPercentage | |
skuThcPotencyMg | batch.calculatedThcWeight | |
batchAromas | batch.aromas | |
skuTotalPrice | Retired. Use item.pricePerUnit * item.quantityPerUnitOfMeasure | |
skuThcPotencyMeasurementType | Retired. batch.calculatedThcPercentage and batch.calculatedThcWeight are both always returned | |
skuDisplayName, skuTags, batchTags | Retired | |
skuConsumableUnitsPerEach, skuEachUnitOfMeasure | Retired | |
avgWeeklyQuantityOrderedPast4Weeks, avgWeeklyQuantityDeliveredPast4Weeks | Retired |
Cannabinoids
v2 returned a fixed field for each of 16 cannabinoids (skuTHC through skuDelta9THC). In v3, each item lists up to
two cannabinoids, each with a dosage and unit:
{
"cannabinoidTypeA": "THC",
"cannabinoidDosageA": 10,
"cannabinoidDosageUnitA": "mg",
"cannabinoidTypeB": "CBD",
"cannabinoidDosageB": 10,
"cannabinoidDosageUnitB": "mg"
}
Dosages are currently always in mg.
Orders
/v3/orders returns one record per order, with only order details. Line item details have moved to
line items, and financial details have moved to invoices. Retailer, item and batch details
are no longer repeated on orders; look them up by ID.
| v2 | v3 | Notes |
|---|---|---|
id | id | |
order | orderNumber | |
orderName | name | |
orderAction | action | |
status | status | See Enum changes |
fromMarketplace | referrer | |
posoNumber | irn | |
orderInventoryStatus, isSampleDemo | same | |
createdDate, createdTimestamp | createdAt | |
updatedDate, updatedTimestamp | updatedAt | |
deliveryDate | deliveryDate | |
sourceWarehouse | warehouseId | Now the warehouse's ID instead of its name. Look up names with GET /v3/warehouses |
retailerId | licensedLocationId | |
siteLicenseNumber | licenseId | Look up the license with GET /v3/retailers/{id}/licenses |
creator | creatorEmail | |
soldBy | assigneeEmail | |
paymentTerms | paymentTerms | Now NET_30, COD and so on instead of "30", "0". See Enum changes |
mustPayPriorBalance | mustPayPriorBalance | |
taxInclusiveLineItemSubtotal (NY) | taxInclusivePricing | Whether the order uses tax-inclusive pricing. Per-line prices are on line items |
notes | notes | |
nabisNotes | nabisManifestNotes | |
manifest | manifestLink | In v2 California this linked to the invoice PDF. In v3 it's the shipping manifest |
pocName, pocPhoneNumber, pocEmail, pocContacts (NY) | contacts[] | Every retailer contact on the order |
retailer, retailerParentOrganization, retailerParentOrganizationId, siteAddress, siteCity, siteZip | Look up the retailer with GET /v3/retailers/{licensedLocationId} | |
retailerCreditRating | Use the retailer's creditClass, or its credit history for the rating at the time of the order | |
organization, brandName | Retired. Every order belongs to your organization | |
daysTillPaymentDue | Retired. Use paymentTerms | |
lastNonPaymentDate, lastNonPaymentReason | Retired | |
titleholderLicenseNumber | Not available in v3 |
Line item fields on orders
These fields moved from orders to GET /v3/line-items. See Line items.
lineItemId, strain, skuCode, unitDescription, units, pricePerUnit, standardPricePerUnit,
lineItemDiscount, sample, lineItemNotes, lineItemUpdatedAt, skuBatchId
These item and batch details moved to GET /v3/inventory/{itemBatchId}. See
Inventory for their new names.
skuName, skuCasePackSize, skuPricePerUnit, skuStrainType, skuIsArchived, inventoryCategory,
inventoryClass, inventoryType, batchNumber, batchExpiration, batchAromas, batchThcPercentage,
batchThcAPercentage, manufacturerLicenseNumber (now batch.licenseNumber)
Financial fields on orders
These fields moved from orders to the order's ORDER invoice from GET /v3/invoices?orderId={id}.
See Invoices.
| v2 | v3 invoice field |
|---|---|
orderSubtotal | subtotal |
orderDiscount | discount |
creditMemo | creditMemo |
creditMemoType, creditMemoNote, creditMemoCreatedAt | creditMemos[].creditMemoType, creditMemos[].notes, creditMemos[].createdAt |
surcharge | surchargeAmount |
exciseTax, orderTaxAmount (NY) | tax |
subtotalCollected | collected |
paymentStatus | paymentStatus |
paidAt, remittedAt | paidAt, remittedAt |
invoiceDueDate (NY) | dueDate |
orderTotal | Retired. Use subtotal + tax + surchargeAmount - creditMemo - discount |
creditMemoCreatedBy | Retired |
Retired calculated fields
| v2 | Calculate it with v3 fields |
|---|---|
lineItemSubtotal | quantity * pricePerUnit |
lineItemSubtotalAfterDiscount | quantity * pricePerUnit - discount - promotionsDiscount - flatDiscountAmount |
lineItemPricePerUnitAfterDiscount | lineItemSubtotalAfterDiscount / quantity |
lineItemWholesaleValue | quantity * itemPricePerUnit |
lineItemPriceDifference | quantity * itemPricePerUnit - lineItemSubtotalAfterDiscount |
wholesaleValue | Sum of lineItemWholesaleValue across the order's line items |
priceReduction | wholesaleValue - subtotal (using the order's ORDER invoice) |
skuTotalPrice | pricePerUnit * quantityPerUnitOfMeasure from the line's inventory item |
orderTsPerLine, totalTsPerLine, skuDisplayName, skuTags, batchTags | Retired, with no replacement |
Order filters
| v2 | v3 | Notes |
|---|---|---|
startCreatedAt, endCreatedAt, startUpdatedAt, endUpdatedAt, startDeliveryDate, endDeliveryDate | same | |
status, action, referrer | same | New values. See Enum changes |
paymentTerms | paymentTerms | Now NET_30, COD and so on |
warehouse, warehouseId (NY) | warehouseId | |
licensedLocationId | New. Only return orders for one retailer | |
paymentStatus | Moved to GET /v3/invoices | |
startLastNonPaymentDate, endLastNonPaymentDate, lastNonPaymentReason | Retired |
Line items
GET /v3/line-items returns the line items on all of your orders, so you can still
sync every line item in one paginated set of requests, as you could with v2's order endpoint. Filter by orderId for a
single order, or by the order's delivery date with startDeliveryDate and endDeliveryDate.
| v2 (order endpoint) | v3 | Notes |
|---|---|---|
lineItemId | id | |
id | orderId | |
skuBatchId | itemBatchId | |
itemId, batchId | New | |
strain | name | |
skuCode | code | |
unitDescription | unit | |
units | quantity | |
pricePerUnit | pricePerUnit | Price per unit before discounts |
standardPricePerUnit | itemPricePerUnit | |
taxInclusiveLineItemSubtotal (NY) | taxInclusivePrice | Now per unit. Multiply by quantity for the line total |
lineItemDiscount | discount, promotionsDiscount, flatDiscountAmount | Split into its three parts |
sample | isSample | |
sampleType | New | |
lineItemNotes | lineItemManifestNotes | |
lineItemUpdatedAt | updatedAt | |
createdAt | New |
Invoices
In v2, an invoice was one record per delivered, unpaid California order. In v3, each record is an invoice, and an order
can have several: the retailer's invoice for the order (groupType ORDER) and any fee invoices (FEE or BASE).
v3 returns every invoice your organization pays or is paid for, paid or not. To get the same list as v2, filter with
groupType=ORDER and collectionStatus=UNFINISHED.
| v2 | v3 | Notes |
|---|---|---|
id | orderId | v2's id was the order's ID. v3's id is the invoice's own ID |
orderNumber, deliveryDate, posoNumber | Look up the order with GET /v3/orders/{orderId} (orderNumber, deliveryDate, irn) | |
name | Use the order's name. v3's invoice name is the invoice's own name | |
dueDate | dueDate | |
subtotal | subtotal | v2 subtracted the discount; v3 doesn't. Use subtotal - discount for v2's value |
discount, creditMemo, tax | same | |
creditMemoType, creditMemoNote, creditMemoCreatedAt | creditMemos[] | |
surcharge | surchargeAmount | |
subtotalCollected, exciseTaxCollected | collected | Combined into a single total |
subtotalDue, due | balanceDue | |
total | Retired. Use subtotal + tax + surchargeAmount - creditMemo - discount | |
organizationName | paidToOrganizationName | |
dispensary | paidByOrganizationName | Or look up the retailer from the order's licensedLocationId |
retailerUuid | Use the order's licensedLocationId | |
siteState | Use the retailer's state | |
region | Use the order's warehouseId and the warehouse's region | |
creatorEmail, mustPayPriorBalance, manifest | Use the order's creatorEmail, mustPayPriorBalance and manifestLink | |
lineItems[] | Use GET /v3/line-items?orderId={orderId} | |
overdue, exciseTaxDueDate, creator, creditMemoCreatedBy, futureNumber, futureDate | Retired | |
number, groupType, paidByOrganizationId, paidToOrganizationId, collectionStatus, netTerms, publicNotes, deduction, writeOff, paymentStatus, paidAt, remittedAt, createdAt, updatedAt | New |
Invoice filters
| v2 | v3 |
|---|---|
orderNumber, irn | Find the order with GET /v3/orders, then filter invoices with orderId |
startDate, endDate (delivery date) | Filter orders by startDeliveryDate and endDeliveryDate, then invoices by orderId |
startDueDate, endDueDate | same |
dispensary, organizationName, organizationId | Retired |
groupType, collectionStatus, paymentStatus, startCreatedAt, endCreatedAt, startUpdatedAt, endUpdatedAt (new) |
Enum changes
| Field | v2 | v3 |
|---|---|---|
Order status | CANCELLED | CANCELED |
Order status | DELAYED, TRANSFERRING | Still valid values, but California orders from before v3 that had them were converted to UNSCHEDULED and IN_TRANSIT |
Order action | New value: RETURN_FROM_RETAILER | |
Order referrer | BRAND, RETAILER | New values: PARTNER, MANUFACTURER |
Order paymentTerms | "0", "7", "14", "15", "30", "45", "60", "90" | COD, NET_7, NET_10, NET_14, NET_15, NET_21, NET_28, NET_30, NET_45, NET_60, NET_90. California orders from before v3 with other day counts were moved to the closest value |
Warehouse region | NORTHERN_CALIFORNIA, SOUTHERN_CALIFORNIA | The US state, for example CA |
| Credit memo type | California credit memo type names | ACCOUNT_CREDIT, FAULTY_HARDWARE_CREDIT, MANUFACTURING_ERROR_CREDIT, MARKETING_CREDIT, PROMOTIONAL_CREDIT, RETURN_CREDIT, ROLLOVER_OVERPAYMENT_CREDIT, SALES_CREDIT, SETTLEMENT_CREDIT |
Inventory inventoryCategory | ALL-IN-ONE (NY) | DISPOSABLE |
License type | New value: TESTING_LABORATORY |