AP bill lines
Lines on a vendor bill. Normally sent nested inside the bill.
The object#
| Field | Type | Description |
|---|---|---|
| idread-only | integer | NolaPro id. |
| apbillid | integer | Apbillid. Id only. |
| amountmoney | string(4dp) | Stored as decimal(19,4). |
| glaccountid | integer | Reference to glaccounts, by id. Write either this or glaccount. |
| glaccount | string(20) | Code for the referenced glaccount, instead of the id. |
| invreceiveid | integer | Invreceiveid. Id only. |
| jobid | integer | Jobid. Id only. |
| costcodeid | integer | Cost code. |
| costcode | string(20) | Code for the referenced costcode, instead of the id. |
| glcategoryid | integer | Glcategoryid. Id only. |
| glcategory | string(20) | The code of the referenced glcategories, instead of the id. Send this or glcategoryid, not both unless they agree. |
| description | string(100) | What the expense line covers. |
| isretainer | integer | 1 when the line is withheld retainage. |
| curratenumber | string(10dp) | Numeric string, 10 decimal places. Not money - do not apply currency rounding. |
| externalid | string(100) | Your own key. Scoped to your company. |
Endpoints#
POST/apbilllines/batch
207400409422
Create many.
Requires scope bills:write.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | Makes the write safely retryable. Any unique string per logical operation; a UUID is fine.
The failure this prevents is specific: the request succeeds, the response is lost to a timeout, you retry, and the customer is billed twice. With a key, the retry returns the original response - same status, same body, same id - and creates nothing.
The body is part of the key's identity. Reusing a key with a different body is a bug on your side, not a retry, and answers 409 idempotency_replay rather than silently handing back the old response. Keys are remembered for 24 hours. |
Body array
| Field | Type | Description |
|---|---|---|
| apbillidrequired | integer | The UNPOSTED bill to code. A posted bill is refused. |
| glaccountid | integer | Account id. Or send glaccount by name. |
| glaccount | string(60) | Account name, matched exactly. |
| amountrequiredmoney | string | How much codes to this account. Cannot take the distribution past the bill total. Decimal STRING, not a JSON number. |
| jobid | integer | Job to cost this to. |
| costcodeid | integer | Cost code. |
| externalid | string(100) | Your own identifier for this record. Stored verbatim and returned on reads; GET /<resource>?externalid=... finds it again. |
When it fails
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request was malformed. |
| 409 | apbill_already_posted | The bill already carries a voucherid. |
| 422 | distribution_exceeds_total | The line would code more than the bill is for. |
| 422 | rule_violation | NolaPro refused the document. message carries its reason. |
| 422 | unknown_value | A reference did not resolve in this company. |
curl -X POST \ 'https://acme.nolapro.com/!/api/v2/apbilllines/batch' \ -H 'Authorization: Bearer $NP_TOKEN' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: your-unique-key' \ -d '[ { "apbillid": 104, "amount": "125.0000", "externalid": "crm-8842" } ]'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/apbilllines/batch'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => json_encode([ [ 'apbillid' => 104, 'amount' => '125.0000', 'externalid' => 'crm-8842', ], ]), ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.post( "https://acme.nolapro.com/!/api/v2/apbilllines/batch", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, json=[ { "apbillid": 104, "amount": "125.0000", "externalid": "crm-8842", }, ], ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/apbilllines/batch', { method: 'POST', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, body: JSON.stringify([ { apbillid: 104, amount: "125.0000", externalid: "crm-8842", }, ]), } ); 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 body = new[] { new { apbillid = 104, amount = "125.0000", externalid = "crm-8842", }, }; var req = new HttpRequestMessage(HttpMethod.Post, "https://acme.nolapro.com/!/api/v2/apbilllines/batch") { Content = JsonContent.Create(body), }; req.Headers.Add("Idempotency-Key", "your-unique-key"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
Response 207
{
"results": [
{
"index": 1,
"status": 1
}
]
}
GET/apbilllines
200403
List ap bill lines.
Requires scope bills:read.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| jobid | query | string | Restrict to one jobid. |
| description | query | string | description. Partial match, case-insensitive. |
| externalid | query | string | Exact match on your own key. |
| 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. |
When it fails
| Status | Code | Meaning |
|---|---|---|
| 403 | insufficient_scope | Token lacks read scope. |
curl \ 'https://acme.nolapro.com/!/api/v2/apbilllines' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/apbilllines'); 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/apbilllines", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/apbilllines', { 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/apbilllines"); res.EnsureSuccessStatusCode();
POST/apbilllines
201400409422
Create.
Requires scope bills:write.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | Makes the write safely retryable. Any unique string per logical operation; a UUID is fine.
The failure this prevents is specific: the request succeeds, the response is lost to a timeout, you retry, and the customer is billed twice. With a key, the retry returns the original response - same status, same body, same id - and creates nothing.
The body is part of the key's identity. Reusing a key with a different body is a bug on your side, not a retry, and answers 409 idempotency_replay rather than silently handing back the old response. Keys are remembered for 24 hours. |
Body
| Field | Type | Description |
|---|---|---|
| apbillidrequired | integer | The UNPOSTED bill to code. A posted bill is refused. |
| glaccountid | integer | Account id. Or send glaccount by name. |
| glaccount | string(60) | Account name, matched exactly. |
| amountrequiredmoney | string | How much codes to this account. Cannot take the distribution past the bill total. Decimal STRING, not a JSON number. |
| jobid | integer | Job to cost this to. |
| costcodeid | integer | Cost code. |
| externalid | string(100) | Your own identifier for this record. Stored verbatim and returned on reads; GET /<resource>?externalid=... finds it again. |
When it fails
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A required field was missing. |
| 409 | apbill_already_posted | The bill already carries a voucherid. |
| 422 | distribution_exceeds_total | The line would code more than the bill is for. |
| 422 | rule_violation | NolaPro refused the document. message carries its reason. |
| 422 | unknown_value | A reference did not resolve in this company. |
curl -X POST \ 'https://acme.nolapro.com/!/api/v2/apbilllines' \ -H 'Authorization: Bearer $NP_TOKEN' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: your-unique-key' \ -d '{ "apbillid": 104, "amount": "125.0000", "externalid": "crm-8842" }'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/apbilllines'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => json_encode([ 'apbillid' => 104, 'amount' => '125.0000', 'externalid' => 'crm-8842', ]), ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.post( "https://acme.nolapro.com/!/api/v2/apbilllines", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, json={ "apbillid": 104, "amount": "125.0000", "externalid": "crm-8842", }, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/apbilllines', { method: 'POST', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, body: JSON.stringify({ apbillid: 104, amount: "125.0000", externalid: "crm-8842", }), } ); 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 body = new { apbillid = 104, amount = "125.0000", externalid = "crm-8842", }; var req = new HttpRequestMessage(HttpMethod.Post, "https://acme.nolapro.com/!/api/v2/apbilllines") { Content = JsonContent.Create(body), }; req.Headers.Add("Idempotency-Key", "your-unique-key"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
Response 201
{
"apbillid": 104,
"glaccountid": 104,
"amount": "125.0000",
"billtotal": "125.0000",
"distributed": "125.0000",
"undistributed": "125.0000"
}
GET/apbilllines/{id}
200404
Retrieve one record.
Requires scope bills:read.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| idrequired | path | integer | The record 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. |
When it fails
| Status | Code | Meaning |
|---|---|---|
| 404 | not_found | No record with that id. |
curl \ 'https://acme.nolapro.com/!/api/v2/apbilllines/104' \ -H 'Authorization: Bearer $NP_TOKEN'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/apbilllines/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/apbilllines/104", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/apbilllines/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/apbilllines/104"); res.EnsureSuccessStatusCode();
Response 200
{
"apbillid": 104,
"amount": "125.0000",
"glaccountid": 104,
"glaccount": "Example glaccount",
"invreceiveid": 104,
"jobid": 104
}
PATCH/apbilllines/{id}
200400404409422
Update.
Requires scope bills:write.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| idrequired | path | integer | The record id. |
| Idempotency-Key | header | string | Makes the write safely retryable. Any unique string per logical operation; a UUID is fine.
The failure this prevents is specific: the request succeeds, the response is lost to a timeout, you retry, and the customer is billed twice. With a key, the retry returns the original response - same status, same body, same id - and creates nothing.
The body is part of the key's identity. Reusing a key with a different body is a bug on your side, not a retry, and answers 409 idempotency_replay rather than silently handing back the old response. Keys are remembered for 24 hours. |
Body
| Field | Type | Description |
|---|---|---|
| amountmoney | string | How much codes to this account. Decimal STRING. |
| glaccountid | integer | Account this line codes to. |
| jobid | integer | Job to cost this to. |
| costcodeid | integer | Cost code. |
| externalid | string(100) | Your own identifier for this record. |
When it fails
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request was malformed. |
| 404 | not_found | No such record, or it belongs to another company. |
| 409 | apbill_already_posted | The bill already carries a voucherid. |
| 409 | stale_record | If-Match did not match; someone else changed it first. |
| 422 | distribution_exceeds_total | The change would code more than the bill is for. |
| 422 | rule_violation | NolaPro refused the change. message carries its reason. |
curl -X PATCH \ 'https://acme.nolapro.com/!/api/v2/apbilllines/104' \ -H 'Authorization: Bearer $NP_TOKEN' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: your-unique-key' \ -d '{ "amount": "12.5000" }'
$ch = curl_init('https://acme.nolapro.com/!/api/v2/apbilllines/104'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NP_TOKEN')], CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'PATCH', CURLOPT_POSTFIELDS => json_encode([ 'amount' => '12.5000', ]), ]); $res = json_decode(curl_exec($ch), true);
import os, requests r = requests.patch( "https://acme.nolapro.com/!/api/v2/apbilllines/104", headers={"Authorization": f"Bearer {os.environ['NP_TOKEN']}"}, json={ "amount": "12.5000", }, ) r.raise_for_status()
const res = await fetch( 'https://acme.nolapro.com/!/api/v2/apbilllines/104', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.NP_TOKEN}` }, body: JSON.stringify({ amount: "12.5000", }), } ); 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 body = new { amount = "12.5000", }; var req = new HttpRequestMessage(HttpMethod.Patch, "https://acme.nolapro.com/!/api/v2/apbilllines/104") { Content = JsonContent.Create(body), }; req.Headers.Add("Idempotency-Key", "your-unique-key"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode();
GL accounts →
name holds the account number and description holds the label. That is backwards from most expectations and it is the single most important thing to know about this resource. Scoped per company, on the companyid column.GL account types →
The account types behind
glaccounts.accounttypeid - asset, liability, income and so on. Fixed by double-entry accounting rather than by configuration, so read-only.