Unit names
Units of measure - Each, M, Pound - used on items, invoice lines and order lines. This list is not company scoped. unitname has no company column, so every company on the install shares one set of units and a change here is visible to all of them immediately.
The object#
| Field | Type | Description |
|---|---|---|
| idread-only | integer | NolaPro id. This is what item and line records store. |
| unitname | string(50) | The unit name as it appears on documents. Trimmed on input. Matching is case insensitive, so each and Each are the same unit. |
| cancelread-only | boolean | True once cancelled. Cancelled records stay referenced by history and keep their id. |
| externalid | string(100) | Your own key for this unit. Unlike the unit itself this is per company, so each company can map its own code onto the same shared unit. |
| lastchangedateread-only | string | Last modification. Drives modifiedsince. |
Endpoints#
List units of measure.
Small and stable - most installs have fewer than fifty. This list is not company scoped. unitname has no company column, so every company on the install shares one set of units and a change here is visible to all of them immediately.
Requires scope items:read.
| Name | In | Type | Notes |
|---|---|---|---|
| unitname | query | string | Partial match, case insensitive. |
| cancel | query | string | Defaults to false, which hides cancelled records. Pass true for only cancelled, or any for both. |
| externalid | query | string | Exact match on your own key. Scoped to your company. |
| modifiedsince | query | string | RFC 3339 UTC timestamp. Returns only records changed since then, including cancelled ones - so cancel defaults to any rather than false when this is used. The response carries _meta.synced_through; store it and send it back next time. |
| include | query | string | Comma-separated extras to embed. Only custom_fields is available: the extra fields this install has defined on the record. Off by default, and an unrecognised value is refused rather than ignored. See Conventions. |
| Status | Code | Meaning |
|---|---|---|
| 403 | insufficient_scope | Token lacks unitnames:read. |
curl \ 'https://acme.nolapro.com/!/api/v2/unitnames' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.get( "https://acme.nolapro.com/!/api/v2/unitnames", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames', { method: 'GET', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var res = await http.GetAsync( "https://acme.nolapro.com/!/api/v2/unitnames"); res.EnsureSuccessStatusCode();
Create a unit of measure.
If a cancelled unit already has this name it is reactivated rather than duplicated, and _meta.warnings says so. If an active one does, you get 409. Matching is case insensitive. This list is not company scoped. unitname has no company column, so every company on the install shares one set of units and a change here is visible to all of them immediately.
Requires scope items:write.
| Name | In | Type | Notes |
|---|---|---|---|
| Field | Type | Description |
|---|---|---|
| unitnamerequired | string(50) | Trimmed. Must not be empty. |
| externalid | string | Optional. Your own key, scoped to your company. |
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | name was missing, empty, or whitespace only. |
| 409 | duplicate | An active unit already has that name. The body names its id. |
| 403 | insufficient_scope | Token lacks unitnames:write. |
curl -X POST \ 'https://acme.nolapro.com/!/api/v2/unitnames' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'POST', ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.post( "https://acme.nolapro.com/!/api/v2/unitnames", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames', { method: 'POST', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var req = new HttpRequestMessage(HttpMethod.Post, "https://acme.nolapro.com/!/api/v2/unitnames"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
{
"id": 1,
"unitname": "Each",
"cancel": false,
"externalid": "EA",
"lastchangedate": "2026-07-31T14:02:11Z"
}
Retrieve one unit.
Requires scope items:read.
| Name | In | Type | Notes |
|---|---|---|---|
| idrequired | path | integer | The unit id. |
| include | query | string | Comma-separated extras to embed. Only custom_fields is available: the extra fields this install has defined on the record. Off by default, and an unrecognised value is refused rather than ignored. See Conventions. |
| Status | Code | Meaning |
|---|---|---|
| 404 | not_found | No unit with that id. |
curl \ 'https://acme.nolapro.com/!/api/v2/unitnames/104' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames/104'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.get( "https://acme.nolapro.com/!/api/v2/unitnames/104", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames/104', { method: 'GET', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var res = await http.GetAsync( "https://acme.nolapro.com/!/api/v2/unitnames/104"); res.EnsureSuccessStatusCode();
{
"id": 1,
"unitname": "Each",
"cancel": false,
"externalid": "EA",
"lastchangedate": "2026-07-31T14:02:11Z"
}
Rename a unit.
The only writable field is name. This renames the unit for every company on the install, including on documents already issued, so _meta.warnings always carries shared_across_companies. Send If-Match if anything else might be editing it.
Requires scope items:write.
| Name | In | Type | Notes |
|---|---|---|---|
| idrequired | path | integer | The unit id. |
| Field | Type | Description |
|---|---|---|
| unitname | string(50) | |
| externalid | string |
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | name was empty or too long. |
| 404 | not_found | No unit with that id. |
| 409 | duplicate | Another active unit already has that name. |
curl -X PATCH \ 'https://acme.nolapro.com/!/api/v2/unitnames/104' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames/104'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'PATCH', ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.patch( "https://acme.nolapro.com/!/api/v2/unitnames/104", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames/104', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var req = new HttpRequestMessage(HttpMethod.Patch, "https://acme.nolapro.com/!/api/v2/unitnames/104"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
{
"id": 1,
"unitname": "Each",
"cancel": false,
"externalid": "EA",
"lastchangedate": "2026-07-31T14:02:11Z"
}
Cancel a unit.
Sets the cancel flag. Nothing is removed, and documents that already reference this unit keep doing so - _meta.warnings reports how many. Needs unitnames:cancel, which unitnames:write does not imply. Cancelling an already cancelled unit succeeds. This list is not company scoped. unitname has no company column, so every company on the install shares one set of units and a change here is visible to all of them immediately.
Requires scope items:cancel.
| Name | In | Type | Notes |
|---|---|---|---|
| idrequired | path | integer | The unit id. |
| Status | Code | Meaning |
|---|---|---|
| 403 | insufficient_scope | Token has unitnames:write but not unitnames:cancel. |
| 404 | not_found | No unit with that id. |
curl -X DELETE \ 'https://acme.nolapro.com/!/api/v2/unitnames/104' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames/104'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'DELETE', ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.delete( "https://acme.nolapro.com/!/api/v2/unitnames/104", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames/104', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var req = new HttpRequestMessage(HttpMethod.Delete, "https://acme.nolapro.com/!/api/v2/unitnames/104"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
{
"id": 1,
"unitname": "Each",
"cancel": false,
"externalid": "EA",
"lastchangedate": "2026-07-31T14:02:11Z"
}
Create many units in one request.
Always answers 207, with one entry per item in the order sent. See Conventions.
Requires scope items:write.
| Name | In | Type | Notes |
|---|---|---|---|
| Field | Type | Description |
|---|---|---|
| unitnamerequired | string(50) |
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The envelope was wrong - not an array, or over the size cap. |
curl -X POST \ 'https://acme.nolapro.com/!/api/v2/unitnames/batch' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/unitnames/batch'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'POST', ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.post( "https://acme.nolapro.com/!/api/v2/unitnames/batch", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/unitnames/batch', { method: 'POST', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, } ); const data = await res.json();
using System.Net.Http.Json; var token = Environment.GetEnvironmentVariable("NP_TOKEN"); using var http = new HttpClient(); http.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); var req = new HttpRequestMessage(HttpMethod.Post, "https://acme.nolapro.com/!/api/v2/unitnames/batch"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();