BlueCollar Projects includes integration APIs that let your other business systems participate directly in BCP workflows. If approvals happen in an external portal, a workflow platform, or a custom application, those systems can approve, reject, and unapprove change orders in BCP programmatically, so your approval process lives wherever your team works, and BCP stays the system of record. Approvals are one use. External systems can also push original and projected estimates onto a project's budget items, and activate or inactivate projects, so an estimating tool or a project-setup system can keep BCP current without anyone re-keying the change.
The APIs are NetSuite RESTlets, which means they use NetSuite's standard, secure integration model:
POST with a JSON body to the RESTlet URL, identified by its script and deploy IDs:https://<account-id>.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=<script-id>&deploy=<deploy-id>
status of 200 on success or 500 on failure (the Budget Item Update API also uses 409 when an accounting period is closed), and an errors array describing anything that went wrong.Every action performed through these APIs runs the same validation and produces the same downstream effects as the equivalent action in the BCP interface: budgets, contracts, purchase orders, and projects update exactly as if a user had done it in the app. That consistency is what makes them safe to automate against.
Drives the approval lifecycle of a BlueCollar Change Request (contract change order).
| Script ID | customscript_bc_ch_req_apprvl_restlet |
| Deploy ID | customdeploy_bc_ch_req_apprvl_restlet_d |
| Method | POST |
| Field | Required | Description |
|---|---|---|
ch_req_id |
Yes | Internal ID of the Change Request |
action |
Yes | approve, reject, unapprove, or editApprovedChangeOrder |
override_projections |
No | true to approve even when the change would exceed contract projections |
files_ids |
For editApprovedChangeOrder only |
Comma-separated file internal IDs of the prepared contract-line CSV files |
approve: approves the change request across both its billing side and its budget side. If any part of the approval fails, the API automatically rolls the change request back to its prior state, so it is never left half-approved. Change requests that have already been processed are rejected with a clear message.reject: rejects a pending change request.unapprove: reverses an approved change request, returning it to Pending Approval. The API validates that the change request is in a status that allows unapproval before making any changes.editApprovedChangeOrder: performs an atomic unapprove → update → re-approve cycle in a single call. This is the same operation the BCP interface uses when editing an approved change order; external callers should prefer approve / reject / unapprove, since the update step requires prepared CSV files. If the re-approval step fails, the response states explicitly that the change request is now in Pending Approval and needs manual re-approval, so nothing is left in an ambiguous state.{0} is the change request ID.) This keeps a retried or duplicated call from applying the same change twice.POST …restlet.nl?script=customscript_bc_ch_req_apprvl_restlet&deploy=customdeploy_bc_ch_req_apprvl_restlet_d
{
"ch_req_id": "1234",
"action": "approve",
"override_projections": false
}
{
"ch_req_id": "1234",
"action": "approve",
"status": 200,
"errors": []
}
Drives the approval lifecycle of a Subcontract Change Request. This allows subcontractor-facing portals or procurement systems to complete the subcontract change workflow end-to-end (including the purchase order updates that approval triggers) without anyone re-keying decisions into BCP.
| Script ID | customscript_bc_subc_ch_req_appr_rest |
| Deploy ID | customdeploy_bc_subc_ch_req_appr_rest_d |
| Method | POST |
| Field | Required | Description |
|---|---|---|
subc_chreq_id |
Yes | Internal ID of the Subcontract Change Request |
action |
Yes | approve, reject, unapprove, or delete |
approve: approves the subcontract change request and applies its purchase order changes.reject: rejects the subcontract change request.unapprove: reverses an approval, including reverting the purchase order line changes the approval made.delete: deletes the subcontract change request.Each action runs through the same controller the Subcontract Change Request screen uses, so permissions, validations, and side effects match the in-app experience exactly.
POST …restlet.nl?script=customscript_bc_subc_ch_req_appr_rest&deploy=customdeploy_bc_subc_ch_req_appr_rest_d
{
"subc_chreq_id": "5678",
"action": "unapprove"
}
{
"subc_chreq_id": "5678",
"action": "unapprove",
"status": 200,
"errors": [],
"result": { }
}
Updates the original and projected estimate, hours, and units on a project's existing budget items. An estimating system or project-controls platform can push revised numbers straight onto the budget, which saves re-keying them in the Budget screen and keeps both systems on the same figures. Budget items themselves are created in the app or by CSV import (see Budget Creation); this API updates values on items that already exist.
| Script ID | customscript_bc_budget_updates_restlet |
| Deploy ID | customdeploy_bc_budget_updates_restlet |
| Method | POST |
| Field | Required | Description |
|---|---|---|
project_id |
Yes | Internal ID of the BlueCollar Project, sent as a string (for example "29974") |
issue_date |
Yes | Date in YYYY-MM-DD format. The history records are written for this date, and projections are read "as of" this date |
budget_items |
Yes | A list of 1 to 25 items to update. Each item has the fields below |
Fields on each item in budget_items:
| Field | Required | Description |
|---|---|---|
budget_id |
Yes | Internal ID of the budget item, as a string. It must belong to the project, and the same budget_id cannot appear twice in one call |
new_original_estimate |
At least one of these six | New Original Estimate amount |
new_original_hours |
New Original Hours | |
new_original_units |
New Original Units | |
new_projected_estimate |
New Projected Estimate amount | |
new_projected_hours |
New Projected Hours | |
new_projected_units |
New Projected Units |
The six value fields are numbers and hold the full new value, not the difference from the current value.
data.budgetStatus, as the budget status list ID (2 is Draft, 3 is Submitted). A call that sends only projected values leaves the status alone.issue_date falls in a closed or A/P-locked accounting period for the project's subsidiary, the whole batch is rejected before anything is written. The response has status 409, and data.periodValidation carries code (CLOSED_PERIOD or AP_LOCKED), closedPeriod, nextOpenPeriod, and subsidiary. A role that holds NetSuite's Override Period Restrictions permission is allowed through; in that case message carries a warning that the override was used.The role behind the integration credentials needs the BlueCollar Global Permission BUDGET / BUDGET_PROJECTIONS at EDIT or FULL.
Read success and status from the response body to decide what happened.
A successful call returns status 200, message "ok", and success true. data contains one block for each type of value you sent: estimate, hours, and units (each with updated, notUpdated, and updateErrors lists), plus projections (with created, notCreated, and creationErrors) whenever a projected value was written, and budgetStatus. Each list entry echoes the change for one item, keyed by itemId (your budget_id); entries can include a few extra bookkeeping fields beyond those shown below. If some items failed, status is still 200 but success is false, message reads "one or more budget_items failed to update", and errors lists the reasons.
A request that fails validation (for example a budget_id that does not belong to the project, or more than 25 items) returns:
{ "data": null, "status": 500, "message": "error", "errors": ["<reason>"], "success": false }
POST …restlet.nl?script=customscript_bc_budget_updates_restlet&deploy=customdeploy_bc_budget_updates_restlet
{
"project_id": "29974",
"issue_date": "2026-07-13",
"budget_items": [
{
"budget_id": "16997",
"new_original_estimate": 10000,
"new_original_hours": 400,
"new_original_units": 20
}
]
}
{
"data": {
"estimate": {
"updated": [{ "itemId": "16997", "newOriginalEstimate": 10000, "oldOriginalEstimate": 8500 }],
"notUpdated": [],
"updateErrors": []
},
"hours": {
"updated": [{ "itemId": "16997", "newOriginalHours": 400, "oldOriginalHours": 350 }],
"notUpdated": [],
"updateErrors": []
},
"units": {
"updated": [{ "itemId": "16997", "newOriginalUnits": 20, "oldOriginalUnits": 18 }],
"notUpdated": [],
"updateErrors": []
},
"projections": {
"created": [{ "itemId": "16997", "nextProjectedEstimate": 10000, "nextProjectedHours": 400, "nextProjectedUnits": 20 }],
"notCreated": [],
"creationErrors": []
},
"budgetStatus": "3"
},
"status": 200,
"message": "ok",
"errors": [],
"success": true
}
Submits a request to inactivate a project, or to activate it again. It is the same request a user submits with the Project Inactivate/Activate tool in the app (see Modifying the Project Record), which lets a project-setup or portfolio system open and close projects in BCP as part of its own workflow.
| Script ID | customscript_bc_proj_inactivate_restlet |
| Deploy ID | customdeploy_bc_proj_inactivate_restlet |
| Method | POST |
| Field | Required | Description |
|---|---|---|
project_id |
Yes | Internal ID of the BlueCollar Project, sent as a string |
action |
Yes | activate or inactivate |
id or review it in NetSuite.data: null and the message "Project is already active, or already has a pending activation request." (or "Project is already inactive, or already has a pending inactivation request."). If a pending request for the opposite action exists, it is switched to the new action instead of creating a second request.The role behind the integration credentials needs the BlueCollar Global Permission CORE / INACTIVATE_PROJECT at FULL to inactivate, or CORE / ACTIVATE_PROJECT at FULL to activate.
On success, status is 200, message is "ok", and data is the request record: id, bcProject, status, submittedBy, submittedOn, failureReason, and isInactivate. Right after creation the record carries id, bcProject, status (Pending), submittedBy, and isInactivate; submittedOn and failureReason are filled in on the record as it is processed. Match on status.name rather than on list IDs. A request that fails validation (a missing or non-numeric project_id, or an action other than activate or inactivate) returns status 500 with the reason in errors.
POST …restlet.nl?script=customscript_bc_proj_inactivate_restlet&deploy=customdeploy_bc_proj_inactivate_restlet
{
"project_id": "1234",
"action": "inactivate"
}
{
"data": {
"id": "512",
"bcProject": { "value": "1234", "text": "1234" },
"status": { "id": "1", "name": "Pending" },
"submittedBy": { "value": "87", "text": "Integration User" },
"isInactivate": true
},
"status": 200,
"message": "ok",
"errors": [],
"success": true
}
The IDs in the example are illustrative.
For the full in-app change order workflows these APIs automate, see Change Order Management, Contract Change Orders, and Subcontract Change Orders.
For budgets, see Budget and Budget Creation. For activating and inactivating projects in the app, see Modifying the Project Record.