Description
n8n-nodes-brightpearl
An n8n community node for the Brightpearl REST API. Covers sales orders, products, price lists, and order custom fields. Works as a standard n8n node and is usableAsTool for AI agents. (AI agents are currently untested with it.)
Disclaimer
Most of the “code” portion of modifying the template into this was written with Claude in a day. Sort of a mixture of proof of concept and making something that works for some quick features I needed. I feel it’s important to note that while I at best provided direction, it did the work of modifying things to ensure it would work with n8n properly. (I had done an attempt before, but some docs were out of date, so I fed what I had into Claude and let it do the rest.)
Claude did attempt to incorporate the rate limit handling per the API docs, but this is an untested option that I am not sure how n8n will operate with as of yet.
BREAKING CHANGES
v0.21.0 Restructures some nodes to make them easier to use in the future. If you see an error, after updating the node, restart your n8n instance in case the workers are using the outdated version.
Migration for existing workflows
Any workflow using one of these old Order operations needs a manual update:
Old (Order resource) New
Order → Add Note Order Note → Create
Order → Get Custom Fields Order Custom Field → Get
Order → Get Custom Field Metadata Order Custom Field → Get Metadata
Order → Update Custom Fields Order Custom Field → Update
Installation
In your n8n instance:
Settings → Community Nodes → Install and enter:
n8n-nodes-brightpearl
Or via CLI on a self-hosted instance:
npm install n8n-nodes-brightpearl
See Cloud Note for more information when it comes to n8n cloud, but TL;DR, I’m using self hosted, so it has a few options that may not work on n8n Cloud.
Authentication
The node supports both Brightpearl auth modes — pick one via the Authentication field at the top of the node:
Private App (three-header auth)
Use this for internal integrations on your own account. Headers sent:
brightpearl-account-tokenbrightpearl-app-refbrightpearl-staff-tokenbrightpearl-app-refbrightpearl-dev-ref
| Credential field | Where to find it |
|—|—|
| Account Code | The subdomain part of your Brightpearl URL |
| Datacenter Hostname | Match your account URL (e.g. use1.brightpearlconnect.com) |
| App Reference | Set when you create the private app under Apps → Private Apps |
| Account Token | Issued when the private app is created |
| Staff Token | The staff token for the user the app runs as |
OAuth2 (public app)
For published apps that need user-authorized access. Standard OAuth2 authorization-code flow against Brightpearl’s endpoints, plus app-ref/dev-ref headers alongside the Bearer token.
Note: If you plan to use the oauth2 when using it with a generic http request node, you’ll need to add two headers manually:
Also use the live api_domain, not the datacenter dropdown, in HTTP Request node URLs:
https://{{$credentials.oauthTokenData.api_domain}}/public-api/{{$credentials.accountCode}}/
Why: on initial OAuth2 authorization Brightpearl returns an apidomain for your account, which may be different from the Datacenter Hostname you picked when creating the credential. n8n stores the returned value under oauthTokenData.apidomain. Hardcoding a different datacenter in the HTTP node URL will route calls to the wrong Brightpearl server and typically returns 401 - {"response":"Invalid instance token: ..."}. Our custom Brightpearl node already uses the live api_domain automatically; this guidance is only for the generic HTTP Request node.
| Credential field | Where to find it |
|—|—|
| Account Code | The target account’s code (used to build the per-account authorize/token URLs) |
| Datacenter Hostname | Datacenter for API calls |
| App Reference | Your registered app reference |
| Developer Reference | Your developer reference (required for public apps) |
| Client ID / Client Secret | From your Brightpearl app registration |
n8n handles the OAuth handshake (authorize URL https://oauth.brightpearl.com/authorize/{account}, token URL https://oauth.brightpearlapp.com/token/{account}) and the refresh-token cycle. The node injects brightpearl-app-ref and brightpearl-dev-ref alongside the Bearer token automatically.
Supported Operations
Order
/sales-order/{id}. Rich response includes orderStatus.name. A Simplify toggle (default on) flattens it into a cleaner shape with statusId/statusName at the top level, rows as an array, etc. Accepts an ID set (single, ascending range 100-200, or comma list 1,2,3) — each order returns as its own output item./order/{id} endpoint (also includes status name and supports Simplify + ID sets). Use whichever endpoint your workflow prefers; results are equivalent for sales orders.orderStatusId: 5 gains orderStatusName: "Complete - Cancelled")> v0.21.0 breaking change: Add Note, Get Custom Fields, Get Custom Field Metadata, and Update Custom Fields moved out of the Order resource into their own top-level resources (Order Note, Order Custom Field). If you have workflows using the old Order operations for these, update the Resource dropdown.
Order Note
POST /order-service/order/{orderId}/note. Optional isPublic, contactId, fileId, addedOn.Order Custom Field
– Builder — entries with an Operation (Add/Replace/Remove), Field Code, Value Type (Text/Date, Number, Boolean, List/Select), and Value. The Value Type ensures the correct JSON type is sent — a mismatch makes Brightpearl return a 500. For List/Select enter the numeric option ID (from Get Metadata).
– Raw JSON Patch — paste a complete RFC 6902 patch array yourself, e.g. [{ "op": "add", "path": "/PCF_SHIPPEDD", "value": "={{ $json.ShippedDate }}" }]. Expressions work inside string values.
Product
Order Row
POST /order-service/order/{orderId}/row (productId + quantity required, optional net/tax/taxCode/productPrice/nominalCode/discountPercentage)PATCH /order-service/order/{orderId}/row/{rowId} with a JSON Patch bodyDELETE /order-service/order/{orderId}/row/{rowId}Contact
/contact-service/contact/{ids}. ID set syntax supported./contact-service/contact-search with filters (name, company, email, phone, isCustomer/isSupplier/isStaff flags, date ranges), columns, sort, pagination, batching.Contact Custom Field
/contact-service/{customer|supplier}/custom-field-meta-data./contact-service/contact/{id}/custom-field./contact-service/contact/{id}/custom-field). Same Builder + Raw JSON Patch input modes as Order Custom Field.Warehouse
/warehouse-service/product-availability/{ids} (all warehouses) or /warehouse-service/warehouse/{id}/product-availability/{ids} (single warehouse). Accepts ID sets.Goods In Note
POST /warehouse-service/order/{orderId}/goods-note/goods-in. Row repeater lets you specify Order Row ID + Quantity (plus optional Batch Number / Expiry Date per row); the node builds Brightpearl’s goodsInNoteRows nested shape automatically.GET /warehouse-service/order/{orderId}/goods-note/goods-in/{idSet}. ID set syntax supported./warehouse-service/goods-in-search with filters (orderId, warehouseId, statusId, createdOn range) and pagination.Goods Out Note
POST /warehouse-service/order/{orderId}/goods-note/goods-out. Row repeater for Order Row ID + Quantity, plus optional release date and a carrier block (name / reference / box count / label URI).POST /warehouse-service/goods-note/goods-out/{id}/event.Reservation
POST /warehouse-service/order/{orderId}/reservation with per-row Order Row ID + Quantity.DELETE /warehouse-service/order/{orderId}/reservation.DELETE /warehouse-service/order/{orderId}/reservation/{orderRowId}.Stock Transfer
GET /warehouse-service/stock-transfer/{idSet}. ID set syntax supported. Leave the Stock Transfer ID field empty to fetch every transfer on the account (Brightpearl’s endpoint supports this natively; no pagination). An Include Created By Contact Name toggle (default on) makes one additional batched call to /contact-service/contact/{idSet} with the deduped createdBy IDs and enriches each transfer with a createdByName field. Contact lookup failures are isolated to _contactLookupError rather than failing the whole operation.OAuth Token
POST https://{datacenter}/{account}/oauth/token/introspect (RFC 7662 token introspection). Authenticates via HTTP Basic (clientid as username, clientsecret as password — per Brightpearl’s “client password” mechanism). Body is form-encoded with just token. Returns the standard introspection fields (active, exp, iat, scope, etc. — note that Brightpearl’s response uses underscore_case keys) and, when Include Computed Fields is on, enriches with secondsUntilExpiry, expiryDateTime, issuedDateTime, and ageSeconds. Only available with OAuth2 authentication. Note: per Brightpearl’s docs, this endpoint does not currently return information about refresh tokens — the Refresh Token option is kept for completeness but will return minimal data.> New in v0.26.0: OAuth-only toggle “Include Refresh Metadata in Output” (top of the node) attaches _tokenRefreshed: true/false to every output item, reflecting whether this execution had to manually refresh the OAuth2 access token. Off by default. The refresh event is also logged to n8n’s logs as [Brightpearl] Manual OAuth token refresh performed for whenever it fires, so you can monitor refresh rate without flipping the output toggle.
Price List
Rate Limiting
Brightpearl enforces a per-account quota. The node handles rate limits automatically:
brightpearl-throttle-time header before retryingRetry-After headerThis is transparent to your workflow — if a transient throttle hits, the node sleeps and tries again with no extra configuration needed.
For batch workloads that intentionally pace themselves (e.g. updating thousands of orders), layer your own Wait node into the workflow. The in-node retry covers transient throttling, not long-term pacing.
> Note on n8n Cloud: the rate-limit retry uses node:timers/promises for sleeping, which n8n Cloud’s sandbox doesn’t allow. This package is therefore configured for self-hosted n8n and is not verifiable for n8n Cloud. If you specifically need Cloud verification, remove the retry loop from GenericFunctions.ts and switch eslint.config.mjs back to the config export from @n8n/node-cli/eslint.
Reference Data Enrichment
Brightpearl search endpoints return rows as positional arrays plus a top-level reference block mapping coded values (status IDs, etc.) to display names. This node converts results to keyed objects and, for any column that declares referenceData, adds a sibling field with the resolved label:
orderStatusId: 5 → orderStatusName: "Complete - Cancelled"There should also be an option to return the raw output if that is helpful for your use case.
Columns ending in Id get a Name counterpart; other columns get a Label suffix. The resolution is automatic for any column Brightpearl flags with reference data.
Links
License
MIT


