Home Integration API reference

Integration API reference

REST API for integrations and external apps

Contacts

List contacts

Returns the active contact directory for the authenticated business, using the same contact visibility rules as the web customer and supplier selectors.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts
AuthenticationBearer token or API key + secret required.
Permissioncustomer.view or customer.view_own when kind=customer; supplier.view or supplier.view_own when kind=supplier.
Visibility rulesUsers with only *_view_own access only see contacts they created or contacts shared through user_contact_access.
CSV behaviorformat=csv streams all matching rows with a UTF-8 BOM and ignores page and per_page.
Success response200 with paginated ContactListItem rows.

Query parameters

ParameterTypeRequiredDescription
kindstringYescustomer or supplier. Customer mode includes contacts with type customer and both; supplier mode includes supplier and both.
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number. Defaults to 1.
qstringNoMinimum 2 characters when sent. Matches name, supplier business name, mobile, landline, alternate number, email, and contact code.
formatstringNojson (default) or csv.

ContactListItem object

FieldTypeDescription
idintegerContact primary key.
typestringcustomer, supplier, or both.
namestringDisplay name used throughout the UI.
supplier_business_namestring | nullSupplier business label when the contact represents a company.
contact_idstring | nullBusiness-facing contact code.
emailstring | nullPrimary email address.
mobilestring | nullPrimary mobile number.
landlinestring | nullLandline number.
city, state, countrystring | nullSaved address summary fields.
tax_numberstring | nullStored tax number.
contact_statusstring | nullUsually active or inactive.
credit_limitnumber | nullCredit limit for the contact.

Top-level JSON response

FieldTypeDescription
dataarray<ContactListItem>Paginated contact rows.
meta.current_pageintegerCurrent paginator page.
meta.last_pageintegerLast available page.
meta.per_pageintegerApplied page size.
meta.totalintegerTotal matching contacts.

Status codes

StatusWhen it happensResponse shape
200Contacts were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user lacks the required customer or supplier permission.{ "message": "Unauthorized" }
422The query string failed validation, such as a 1-character q or missing kind.Laravel validation JSON or an explicit message.

Get contact

Returns one contact record for the current business. This endpoint extends ContactListItem with address, balance, custom fields, and export-related details.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}
AuthenticationBearer token or API key + secret required.
PermissionAny of supplier.view, supplier.view_own, customer.view, or customer.view_own.
Visibility rulesIf the user only has *_view_own access, the contact must be created by that user or shared through user_contact_access.
CSV behaviorformat=csv streams a single row with a data_json column whose value matches the JSON data object.
Success response200 with a ContactDetail object.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

ContactDetail additional fields

FieldTypeDescription
prefix, first_name, middle_name, last_namestring | nullName parts stored on the contact form.
address_line_1, address_line_2, zip_code, shipping_addressstring | nullAddress fields and shipping address.
alternate_numberstring | nullSecondary phone number.
balancenumber | nullCurrent contact balance snapshot.
pay_term_numberinteger | nullPay term value.
pay_term_typestring | nullPay term unit such as days or months.
dobstring | nullDate of birth in Y-m-d format.
is_defaultbooleanWhether the contact is the system default contact.
created_byinteger | nullUser id that created the contact.
customer_group_idinteger | nullLinked customer group id.
created_at, updated_atstring | nullISO-8601 timestamps.
custom_field1 to custom_field10string | nullBusiness-defined custom contact fields.
custom_field_labelsobjectBusiness label map keyed like custom_field_1.
shipping_custom_field_detailsarray | object | nullStored shipping custom field payload.
is_exportbooleanWhether export-specific fields are enabled for the contact.
export_custom_field_1 to export_custom_field_6string | nullExport custom fields saved when is_export is enabled.

Top-level JSON response

FieldTypeDescription
dataContactDetailFull contact payload for the requested row.

Status codes

StatusWhen it happensResponse shape
200The contact was returned successfully.JSON { "data": ContactDetail } or CSV download.
403The token user lacks view access to this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }

Contact activities

Lists the audit/activity log rows for a single contact, using the same underlying Spatie activity stream shown in the web contact Activities tab.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/activities
PermissionSame access and visibility rules as Get contact.
Sort orderNewest activity first.
CSV behaviorformat=csv streams all matching activities and ignores pagination. Nested causer and properties values are JSON-encoded in cells.
Success response200 with paginated activity rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number. Defaults to 1.
qstringNoMinimum 2 characters when sent. Matches description, numeric activity id, or the causer name, username, or email.
formatstringNojson (default) or csv.

ContactActivity object

FieldTypeDescription
idintegerActivity primary key.
descriptionstringHuman-readable activity message.
eventstring | nullEvent name stored by the activity log package.
log_namestring | nullActivity log channel.
created_atstring | nullISO-8601 timestamp.
causerobject | nullWhen present: { "id": integer, "name": string }.
propertiesobject | array | nullDecoded activity metadata payload.

Top-level JSON response

FieldTypeDescription
data.contact_idintegerContact id from the path.
data.activitiesarray<ContactActivity>Paginated activity rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.qstring | nullEchoed search term when present.

Status codes

StatusWhen it happensResponse shape
200Activity rows were returned successfully.JSON { "data": { ... }, "meta": { ... } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The query string failed validation, such as a 1-character q.Laravel validation JSON or an explicit message.

Validate contact mobile (duplicate check)

Checks whether a mobile number already exists in the current business, using the same duplicate-check rule as the web contact form.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/validate/mobile
PermissionAny permission that allows listing or viewing contacts.
Match ruleChecks contacts.mobile with the same suffix-match logic used by the web duplicate checker.
CSV behaviorformat=csv returns one row with a data_json column.
Success response200 with duplicate-check details.

Query parameters

ParameterTypeRequiredDescription
mobile_numberstringYesMobile number to check.
contact_idintegerNoExisting contact id to exclude during edit flows. It must exist in the same business and be visible to the token user.
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
data.is_mobile_existsbooleantrue when one or more matching contacts were found.
data.msgstringTranslated duplicate-check message returned by the controller.
data.matching_contact_namesarray<string>Matched contact display names.

Status codes

StatusWhen it happensResponse shape
200The duplicate check completed successfully.{ "data": { "is_mobile_exists": bool, "msg": string, "matching_contact_names": [...] } }
403The token user cannot use contact validation endpoints or cannot access the excluded contact_id.{ "message": "Unauthorized" }
422The query string failed validation or the excluded contact_id is invalid.Laravel validation JSON or an explicit message.

Validate contact tax number (duplicate check)

Checks whether a tax number already exists in the current business, matching the same duplicate rule used by the web contact form.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/validate/tax-number
PermissionSame access gate as Validate contact mobile.
Match ruleExact tax-number match within the current business.
CSV behaviorformat=csv returns one row with a data_json column.
Success response200 with duplicate-check details.

Query parameters

ParameterTypeRequiredDescription
tax_numberstringYesTax number to check.
contact_idintegerNoExisting contact id to exclude during edit flows. It must exist in the same business and be visible to the token user.
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
data.is_tax_number_existsbooleantrue when one or more matching contacts were found.
data.msgstringTranslated duplicate-check message. Empty when there is no duplicate.
data.matching_contact_namesarray<string>Matched contact display names.

Status codes

StatusWhen it happensResponse shape
200The duplicate check completed successfully.{ "data": { "is_tax_number_exists": bool, "msg": string, "matching_contact_names": [...] } }
403The token user cannot use contact validation endpoints or cannot access the excluded contact_id.{ "message": "Unauthorized" }
422The query string failed validation or the excluded contact_id is invalid.Laravel validation JSON or an explicit message.

Contact map (geocoded positions)

Returns geocoded contacts for the current business, using the same active-contact and visibility rules as the web contact map.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/map
PermissionAt least one of supplier.view, supplier.view_own, customer.view, or customer.view_own.
Source rowsOnly active contacts whose position field can be parsed into latitude and longitude values.
CSV behaviorformat=csv streams all matching rows and is not paginated.
Success response200 with contact map rows plus a small meta block.

Query parameters

ParameterTypeRequiredDescription
kindstringNoall (default), customer, or supplier.
contact_ids[]array<integer>NoOptional contact ids to narrow the result set. Contacts still have to be visible to the token user.
formatstringNojson (default) or csv.

ContactMapRow object

FieldTypeDescription
idintegerContact primary key.
namestringDisplay name.
contact_idstring | nullBusiness-facing contact code.
typestringcustomer, supplier, or both.
supplier_business_namestring | nullSupplier business label when present.
shipping_addressstring | nullSaved shipping address.
latitude, longitudenumberParsed coordinates from the contact position field.

Top-level JSON response

FieldTypeDescription
dataarray<ContactMapRow>Matching map markers.
meta.kindstringApplied kind filter.
meta.totalintegerTotal returned markers.

Status codes

StatusWhen it happensResponse shape
200Map rows were returned successfully.JSON { "data": [...], "meta": { "kind": string, "total": integer } } or CSV download.
403The token user lacks permission to read contacts.{ "message": "Unauthorized" }

Import contacts CSV/XLS (POST multipart)

Imports contacts from the same spreadsheet template used by the web Import contacts flow, including creation of opening balance transactions when present in the sheet.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/contacts/import
Permissionsupplier.create or customer.create.
Request encodingmultipart/form-data
Subscription ruleReturns 402 when package enforcement is enabled and the business subscription is not active.
Demo modeReturns 403 in demo environments.
Success response201 with imported row count.

Required headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_ACCESS_TOKEN
Content-TypeYesmultipart/form-data
AcceptNoRecommended: application/json.

Multipart fields

FieldTypeRequiredDescription
contacts_csvfileYesSpreadsheet file in csv, xls, xlsx, or txt format, up to 15360 KB.

Import sheet rules

RuleDescription
Template layoutThe first row is a header row and is skipped. Each data row must follow the same column order as the downloadable contact import template.
Contact type columnThe importer expects the same numeric contact-type markers as the web template (1, 2, 3).
Validation behaviorThe importer enforces the same rules as the web flow, including duplicate contact_id, invalid email format, bad column counts, and invalid type mappings.
Side effectsSuccessful imports create contacts, optionally create opening balance transactions, and record the same imported activity trail as the web import flow.

Success response

FieldTypeDescription
messagestringLocalized import success message.
data.importedintegerNumber of contacts created from the uploaded sheet.

Status codes

StatusWhen it happensResponse shape
201The spreadsheet was imported successfully.{ "message": string, "data": { "imported": integer } }
402The business is not subscribed when package enforcement is active.{ "message": string }
403Demo mode or missing create permission.{ "message": string }
422The uploaded file is missing, invalid, empty, or the importer rejected one or more rows.Laravel validation JSON or { "message": string }.

Contact outstanding balance

Returns the current due amount for one contact using the same balance calculation shown in the web contact view.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/balance
PermissionSame access and visibility rules as Get contact.
CalculationUses the same due calculation as the web contact screen, including sells, purchases, returns, opening balance, and related payments.
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with balance data.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
data.contact_idintegerContact id from the path.
data.duenumberRaw outstanding amount.
data.due_formattedstringCurrency-formatted due amount.

Status codes

StatusWhen it happensResponse shape
200The balance was returned successfully.{ "data": { "contact_id": integer, "due": number, "due_formatted": string } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }

Contact opening balance

Returns the contact's opening-balance transaction when one exists. This is a read-only lookup; the registered integration route is GET only.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/opening-balance
PermissionSame access and visibility rules as Get contact.
BehaviorReturns the single system opening-balance transaction for the contact when present; otherwise transaction is null.
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with opening balance state.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

OpeningBalanceTransaction object

FieldTypeDescription
idintegerTransaction primary key.
ref_nostring | nullOpening-balance reference number.
final_totalnumberOpening-balance amount.
total_paidnumberTotal payments applied to the opening balance.
duenumberOutstanding amount after payments.
due_formattedstringCurrency-formatted due amount.
payment_statusstring | nullPayment status computed for the transaction.
transaction_datestring | nullISO-8601 transaction timestamp.
location_idinteger | nullBusiness location id on the transaction.
location_namestring | nullBusiness location name.

Top-level JSON response

FieldTypeDescription
data.contact_idintegerContact id from the path.
data.has_opening_balancebooleanWhether an opening-balance transaction exists.
data.transactionOpeningBalanceTransaction | nullTransaction payload when one exists.

Status codes

StatusWhen it happensResponse shape
200The opening-balance state was returned successfully.{ "data": { "contact_id": integer, "has_opening_balance": bool, "transaction": object|null } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }

Contact ledger

Returns the contact ledger for a date range using the same ledger engine as the web contact ledger screen.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/ledger
PermissionSame access and visibility rules as Get contact.
Ledger formatsformat_1 (default), format_2, or csv.
CSV behaviorformat=csv streams all filtered ledger lines using the format-1 layout. The summary block stays JSON-only.
Success response200 with ledger lines, summary totals, and echoed filter metadata.

Query parameters

ParameterTypeRequiredDescription
start_datestringYesInclusive start date in Y-m-d format.
end_datestringYesInclusive end date in Y-m-d format and must be on or after start_date.
location_idintegerNoOptional business location filter.
formatstringNoformat_1, format_2, or csv. Defaults to format_1.
qstringNoMinimum 2 characters when sent. Filters ledger lines only by reference, type, location, payment status, note, or numeric transaction id.

ContactLedgerLine object

FieldTypeDescription
datestring | nullISO-8601 ledger row date.
ref_nostringTransaction or payment reference number.
typestringLedger row type label from the web ledger.
locationstringBusiness location label.
payment_statusstringPayment status text for transaction rows.
debit, creditnumber | nullLedger debit and credit amounts.
balancestringRunning balance display string.
notestringPlain-text notes/other row details.
transaction_id, transaction_typeinteger | stringPresent when the row maps to a transaction record.
payment_method_keystring | nullPresent for payment rows when the ledger engine exposes the payment method.
final_total, total_due, total_paidnumber | nullInvoice-style totals used by the alternate ledger layout.
due_datestring | nullDue date when the ledger row represents an invoice-like transaction.

ContactLedgerSummary object

FieldTypeDescription
start_date, end_datestringApplied reporting window.
beginning_balancenumberBalance before the requested period.
balance_duenumberBalance due for the requested period.
total_invoice, total_purchase, total_paid, total_reverse_payment, ledger_discountnumberTotals for the requested period.
all_total_invoice, all_invoice_paid, all_total_purchase, all_purchase_paid, all_balance_due, all_ledger_discountnumberOverall ledger totals for the contact.

Top-level JSON response

FieldTypeDescription
data.contact_idintegerContact id from the path.
data.linesarray<ContactLedgerLine>Filtered ledger lines.
data.summaryContactLedgerSummaryTotals block for the selected range.
meta.start_date, meta.end_datestringEchoed date filters.
meta.location_idinteger | nullEchoed location filter.
meta.formatstringApplied ledger layout.
meta.qstring | nullEchoed search term when present.

Status codes

StatusWhen it happensResponse shape
200The ledger was returned successfully.JSON { "data": { ... }, "meta": { ... } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The date range or search query failed validation.Laravel validation JSON or an explicit message.

Send contact ledger (email)

Builds the same ledger PDF used by the web Send ledger action and emails it using the business notification settings.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/contacts/{id}/ledger/send
PermissionSame access and visibility rules as Get contact and Contact ledger.
Request encodingapplication/json
Demo modeReturns 403 in demo environments.
Success response200 with a success message after the email is queued/sent.

Required headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_ACCESS_TOKEN
Content-TypeYesapplication/json
AcceptNoRecommended: application/json.

Request body

FieldTypeRequiredDescription
to_emailstringYesRecipient email list, matching the web ledger email flow.
subjectstringYesEmail subject line.
email_bodystringYesEmail body text. Template tags and {balance_due} work the same way as in the web notification template.
ledger_formatstringYesformat_1, format_2, format_3, or format_4.
start_date, end_datestringYesDate range in Y-m-d format for the attached ledger.
cc, bccstring | nullNoOptional CC and BCC address lists.
location_idinteger | nullNoOptional location filter for the generated ledger.

Status codes

StatusWhen it happensResponse shape
200The ledger email was sent successfully.{ "message": string }
403Demo mode or the token user cannot access this contact.{ "message": string }
404The contact or related business record was not found.{ "message": "Not found" } or equivalent translated message.
422The payload failed validation or email/PDF generation failed.Laravel validation JSON or { "message": string }.

Contact ledger discounts

Lists ledger discount rows for a single contact. It is the same data contract as the global ledger-discount list, but with the contact fixed by the path.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/ledger-discounts
PermissionSame ledger-style read access as Contact ledger, plus visibility to the contact from the path.
Contact bindingThe controller forces contact_id={id}; any query-string contact_id is ignored.
CSV behaviorformat=csv streams all matching rows and ignores pagination.
Success response200 with paginated LedgerDiscountRow rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number.
start_date, end_datestringNoOptional business-date range on transaction_date. Both must be provided together.
qstringNoMinimum 2 characters when sent. Matches note text, numeric id, or contact name.
formatstringNojson (default) or csv.

LedgerDiscountRow object

FieldTypeDescription
idintegerLedger-discount transaction id.
contact_idintegerLinked contact id.
contact_namestringLinked contact display name.
contact_typestringcustomer, supplier, or both.
sub_typestringsell_discount or purchase_discount.
amountnumberDiscount amount.
notestring | nullSaved note on the discount transaction.
transaction_datestring | nullISO-8601 transaction timestamp.
created_byinteger | nullUser id that created the ledger discount.

Top-level JSON response

FieldTypeDescription
dataarray<LedgerDiscountRow>Paginated discount rows for the bound contact.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.

Status codes

StatusWhen it happensResponse shape
200Ledger discount rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user cannot read this contact's ledger data.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The date filters or search query failed validation.Laravel validation JSON or an explicit message.

List ledger discounts

Lists all ledger discount rows visible to the authenticated user across accessible contacts.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/ledger-discounts
PermissionSame ledger-style read access as Contact ledger.
Visibility rulesRows are limited to contacts the token user can access.
CSV behaviorformat=csv streams all matching rows and ignores pagination.
Success response200 with paginated LedgerDiscountRow rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number.
contact_idintegerNoOptional contact filter. The contact must exist in the same business and be visible to the token user.
start_date, end_datestringNoOptional business-date range on transaction_date. Both must be provided together.
qstringNoMinimum 2 characters when sent. Matches note text, numeric id, or contact name.
formatstringNojson (default) or csv.

Top-level JSON response

FieldTypeDescription
dataarray<LedgerDiscountRow>Paginated ledger discount rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.

Status codes

StatusWhen it happensResponse shape
200Ledger discount rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user lacks ledger-style read access or cannot access the filtered contact.{ "message": "Unauthorized" }
404The supplied contact_id does not exist in the current business.{ "message": "Not found" }
422The date filters or search query failed validation.Laravel validation JSON or an explicit message.

Get ledger discount

Returns one ledger discount row when the transaction belongs to the current business and its contact is visible to the token user.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/ledger-discounts/{id}
PermissionSame ledger-style read access as List ledger discounts.
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with a LedgerDiscountRow object.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
dataLedgerDiscountRowLedger discount payload for the requested transaction.

Status codes

StatusWhen it happensResponse shape
200The ledger discount was returned successfully.{ "data": LedgerDiscountRow } or CSV download.
403The token user lacks access to the linked contact.{ "message": "Unauthorized" }
404The transaction is not a ledger discount in the current business.{ "message": "Not found" }

Create ledger discount

Creates a ledger-discount transaction using the same business rules as the web Add discount flow in the contact ledger.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/ledger-discounts
PermissionSame ledger-style read access as List ledger discounts, plus access to the supplied contact.
Request encodingapplication/json
Demo modeReturns 403 in demo environments.
Success response201 with the created LedgerDiscountRow.

Required headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_ACCESS_TOKEN
Content-TypeYesapplication/json
AcceptNoRecommended: application/json.

Request body

FieldTypeRequiredDescription
contact_idintegerYesContact to apply the discount to. The contact must belong to the current business and be visible to the token user.
datestringYesTransaction date in the business date format accepted by the app.
amountnumberYesDiscount amount.
notestring | nullNoOptional free-text note.
sub_typestring | nullConditionalRequired only when the contact type is both; allowed values are sell_discount or purchase_discount.

Status codes

StatusWhen it happensResponse shape
201The ledger discount was created successfully.{ "message": string, "data": LedgerDiscountRow }
403Demo mode or the token user cannot access the supplied contact.{ "message": string }
404The supplied contact_id does not exist in the current business.{ "message": string }
422The payload failed validation or a both-type contact was sent without a valid sub_type.Laravel validation JSON or { "message": string, "errors": { ... } }.
500The create transaction failed unexpectedly.{ "message": "something_went_wrong" }

Update ledger discount

Updates an existing ledger-discount transaction. This matches the web edit flow and is restricted to business admins.

Endpoint summary

PropertyValue
MethodsPATCH or PUT
Path/api/v1/integration/ledger-discounts/{id}
PermissionBusiness admin access plus the same contact visibility rules as List ledger discounts.
Request encodingapplication/json
Demo modeReturns 403 in demo environments.
Success response200 with the updated LedgerDiscountRow.

Request body

FieldTypeRequiredDescription
datestringYesTransaction date in the business date format accepted by the app.
amountnumberYesUpdated discount amount.
notestring | nullNoOptional replacement note.
sub_typestring | nullNoOptional replacement sub-type: sell_discount or purchase_discount.

Status codes

StatusWhen it happensResponse shape
200The ledger discount was updated successfully.{ "message": string, "data": LedgerDiscountRow }
403Demo mode, the token user is not an admin, or the linked contact is not visible.{ "message": string }
404The ledger discount id does not exist in the current business.{ "message": "Not found" }
422The payload failed validation or a business rule blocked the update.Laravel validation JSON or { "message": string }.
500The update transaction failed unexpectedly.{ "message": "something_went_wrong" }

Delete ledger discount

Deletes a ledger-discount transaction. This matches the web delete action and is restricted to business admins.

Endpoint summary

PropertyValue
MethodDELETE
Path/api/v1/integration/ledger-discounts/{id}
PermissionBusiness admin access plus the same contact visibility rules as List ledger discounts.
Demo modeReturns 403 in demo environments.
Success response200 with a success message.

Status codes

StatusWhen it happensResponse shape
200The ledger discount was deleted successfully.{ "message": string }
403Demo mode, the token user is not an admin, or the linked contact is not visible.{ "message": string }
404The ledger discount id does not exist in the current business.{ "message": "Not found" }
500The delete transaction failed unexpectedly.{ "message": "something_went_wrong" }

List contact purchases

Lists purchase rows for one supplier contact. This endpoint delegates to the same purchase index used by the dedicated purchases module.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/purchases
PermissionSame purchase-list permission and location rules as the dedicated List purchases endpoint.
Contact ruleThe contact must exist, be visible to the token user, and have type supplier or both.
CSV behaviorformat=csv streams the same purchase export used by the dedicated purchases list and ignores pagination.
Success response200 with the same list-row contract as List purchases, with contact_id fixed by the path.

Query parameters

ParameterTypeRequiredDescription
per_page, pageintegerNoStandard purchase-list pagination controls.
location_idintegerNoOptional location filter.
start_date, end_datestringNoOptional purchase date range.
payment_status, statusstringNoStandard purchase filters inherited from the main purchases list.
qstringNoOptional purchase search term when supported by the delegated index.
formatstringNojson (default) or csv.

Top-level response

FieldTypeDescription
dataarraySame purchase rows documented in the dedicated purchases module. Each row includes the purchase id, reference fields, dates, totals, and related contact/location summaries.
metaobjectStandard paginator metadata from the delegated purchases index.

Status codes

StatusWhen it happensResponse shape
200Purchase rows were returned successfully.Same JSON or CSV shape as List purchases.
403The token user cannot access the contact or cannot use the purchase list endpoint.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The contact exists but is not a supplier-type contact, or delegated purchase filters failed validation.Laravel validation JSON or { "message": string }.

List contact sales

Lists sell rows for one customer contact. This endpoint delegates to the same sell index used by the dedicated sells module.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/sells
PermissionSame sell-list permission and location rules as the dedicated List sells endpoint.
Contact ruleThe contact must exist, be visible to the token user, and have type customer or both.
CSV behaviorformat=csv streams the same sell export used by the dedicated sells list and ignores pagination.
Success response200 with the same list-row contract as List sells, with contact_id fixed by the path.

Query parameters

ParameterTypeRequiredDescription
per_page, pageintegerNoStandard sell-list pagination controls.
location_id, created_byintegerNoOptional sell filters inherited from the main sells list.
start_date, end_datestringNoOptional sale date range.
payment_status, only_shipments, is_direct_salestring | booleanNoStandard sell filters supported by the delegated index.
qstringNoOptional sell search term when supported by the delegated index.
formatstringNojson (default) or csv.

Top-level response

FieldTypeDescription
dataarraySame sell rows documented in the dedicated sells module. Each row includes invoice identifiers, dates, totals, payment status, and related contact/location summaries.
metaobjectStandard paginator metadata from the delegated sells index.

Status codes

StatusWhen it happensResponse shape
200Sell rows were returned successfully.Same JSON or CSV shape as List sells.
403The token user cannot access the contact or cannot use the sell list endpoint.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The contact exists but is not a customer-type contact, or delegated sell filters failed validation.Laravel validation JSON or { "message": string }.

Supplier stock report

Returns the aggregated stock report for a supplier contact, matching the supplier Stock report tab in the web contact screen.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/supplier-stock
Permissionsupplier.view or supplier.view_own, plus visibility to the contact from the path.
Contact ruleThe contact must be type supplier or both.
CSV behaviorformat=csv streams all matching rows and ignores pagination.
Success response200 with paginated SupplierStockRow rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 50.
pageintegerNoPagination page number.
location_idintegerNoOptional business location filter.
qstringNoMinimum 2 characters when sent. Matches product name, variation name, variation template name, sub_sku, or numeric variation id.
formatstringNojson (default) or csv.

SupplierStockRow object

FieldTypeDescription
variation_idintegerVariation primary key.
product_labelstringCombined product/variation label with SKU.
product_name, variation_name, product_variation_namestring | nullProduct and variation naming fields used in the report.
product_typestringProduct type such as single or variable.
sub_skustring | nullVariation SKU.
unitstring | nullUnit short label.
purchase_quantity, total_quantity_returned, total_quantity_sold, total_quantity_transferednumberMovement quantities used by the stock report.
stock_valuenumberCurrent stock valuation.
current_stocknumberCurrent on-hand quantity.

Top-level JSON response

FieldTypeDescription
dataarray<SupplierStockRow>Paginated supplier stock rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.location_idinteger | nullEchoed location filter.
meta.qstring | nullEchoed search term when present.

Status codes

StatusWhen it happensResponse shape
200Supplier stock rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user cannot access this supplier contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The contact exists but is not a supplier-type contact, or the query string failed validation.Laravel validation JSON or an explicit message.

Contact subscriptions (recurring invoices)

Lists recurring subscription rows for a single customer contact, using the same subscription mapper and schedule logic as the web subscriptions table.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/subscriptions
Module requirementThe business must have the subscription module enabled.
Permissionsell.view or direct_sell.access, plus permitted-location access.
Contact ruleThe contact must be type customer or both and be visible to the token user.
CSV behaviorformat=csv streams all matching rows and ignores pagination.
Success response200 with paginated SubscriptionRow rows.

Query parameters

ParameterTypeRequiredDescription
per_page, pageintegerNoStandard subscription-list pagination controls.
start_date, end_datestringNoOptional parent transaction date range in Y-m-d. Both must be provided together.
location_idintegerNoOptional location filter, still restricted by permitted locations.
qstringNoMinimum 2 characters when sent. Matches invoice number, subscription number, location name, or numeric transaction id.
formatstringNojson (default) or csv.

SubscriptionRow object

FieldTypeDescription
idintegerRecurring parent sale id.
contact_idinteger | nullLinked customer id.
contact_namestring | nullLinked customer display name.
transaction_datestring | nullISO-8601 parent transaction timestamp.
invoice_no, subscription_nostring | nullParent invoice number and subscription number.
location_idinteger | nullBusiness location id.
location_namestring | nullBusiness location label.
is_direct_salebooleanWhether the recurring sale is a direct sale.
recur_stopped_onstring | nullDate when recurring generation was stopped.
is_stoppedbooleanConvenience flag derived from recur_stopped_on.
recur_interval, subscription_repeat_on, recur_repetitionsinteger | nullRecurring schedule settings saved on the parent sale.
recur_interval_type, recur_interval_labelstring | nullRecurring cadence and its UI-friendly label.
generated_invoicesarray<object>Generated child invoices. Each item contains id, invoice_no, and transaction_date.
generated_invoice_countintegerNumber of generated child invoices.
last_generated_atstring | nullISO-8601 timestamp of the most recently generated child invoice.
upcoming_invoice_datestring | nullNext scheduled invoice date, or null when the subscription is stopped.

Top-level JSON response

FieldTypeDescription
dataarray<SubscriptionRow>Paginated subscription rows for the bound contact.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.location_id, meta.start_date, meta.end_date, meta.qmixedEchoed filters when present.

Status codes

StatusWhen it happensResponse shape
200Subscription rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The subscription module is disabled, the token user lacks permission, or permitted locations block the request.{ "message": string }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The contact exists but is not a customer-type contact, or the query string failed validation.Laravel validation JSON or an explicit message.

List subscriptions (recurring invoices)

Lists all recurring subscription rows visible to the authenticated user across permitted locations.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/subscriptions
Module requirementThe business must have the subscription module enabled.
Permissionsell.view or direct_sell.access, plus permitted-location access.
CSV behaviorformat=csv streams all matching rows and ignores pagination.
Success response200 with paginated SubscriptionRow rows.

Query parameters

ParameterTypeRequiredDescription
per_page, pageintegerNoStandard pagination controls.
start_date, end_datestringNoOptional parent transaction date range in Y-m-d. Both must be provided together.
location_idintegerNoOptional location filter within permitted locations.
contact_idintegerNoOptional customer filter for the current business.
qstringNoMinimum 2 characters when sent. Matches invoice number, subscription number, location name, customer name, or numeric transaction id.
formatstringNojson (default) or csv.

Top-level JSON response

FieldTypeDescription
dataarray<SubscriptionRow>Paginated subscription rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.location_id, meta.contact_id, meta.start_date, meta.end_date, meta.qmixedEchoed filters when present.

Status codes

StatusWhen it happensResponse shape
200Subscription rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The subscription module is disabled, the token user lacks permission, or permitted locations block the request.{ "message": string }
422The query string failed validation, such as an invalid date range or a 1-character q.Laravel validation JSON or an explicit message.

Get subscription (recurring parent sale)

Returns one recurring parent sale subscription when it belongs to the current business and falls within the user's permitted locations.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/subscriptions/{id}
Module requirementThe business must have the subscription module enabled.
Permissionsell.view or direct_sell.access, plus permitted-location access.
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with a SubscriptionRow object.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
dataSubscriptionRowMapped recurring subscription payload, including generated invoice details.

Status codes

StatusWhen it happensResponse shape
200The subscription was returned successfully.{ "data": SubscriptionRow } or CSV download.
403The subscription module is disabled or the token user lacks permission.{ "message": string }
404The subscription id does not exist in the current business or falls outside permitted locations.{ "message": "Not found" }
500An unexpected load error occurred while mapping the subscription.{ "message": string }

Toggle subscription recurring (stop / resume)

Stops or resumes automatic invoice generation for a recurring parent sale by toggling its recur_stopped_on value.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/subscriptions/{id}/toggle-recurring
Module requirementThe business must have the subscription module enabled.
Permissionsell.create, plus permitted-location access.
Request bodyNo request body is required.
Demo modeReturns 403 in demo environments.
Success response200 with the updated SubscriptionRow.

Behavior

Current stateEffect
recur_stopped_on is emptyThe controller sets it to the current timestamp, stopping future recurring invoice generation.
recur_stopped_on already has a valueThe controller clears it, resuming the recurring schedule.

Status codes

StatusWhen it happensResponse shape
200The recurring state was toggled successfully.{ "data": SubscriptionRow }
403Demo mode, the subscription module is disabled, or the token user lacks sell.create.{ "message": string }
404The subscription id does not exist in the current business or falls outside permitted locations.{ "message": "Not found" }

List contact documents & notes

Lists documents and notes attached to a contact, matching the visibility rules from the web Documents & notes tab.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/documents-and-notes
PermissionSame access and visibility rules as Get contact.
Visibility rulesPublic notes are visible to all authorized users. Private notes are only visible when created_by is the authenticated user.
CSV behaviorformat=csv streams all matching rows and ignores pagination. Nested media values are JSON-encoded in cells.
Success response200 with paginated ContactDocumentNoteRow rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number.
qstringNoMinimum 2 characters when sent. Matches heading, description, or numeric document id.
formatstringNojson (default) or csv.

ContactDocumentNoteRow object

FieldTypeDescription
idintegerDocument/note primary key.
headingstringSaved heading/title.
descriptionstring | nullSaved note body.
is_privatebooleanWhether the note is private to its creator.
created_byinteger | nullCreator user id.
created_by_namestring | nullCreator full name.
created_at, updated_atstring | nullISO-8601 timestamps.
mediaarray<object>Attached files. Each item includes id, file_name, display_name, and url.
media_countintegerTotal number of attached media rows.

Top-level JSON response

FieldTypeDescription
dataarray<ContactDocumentNoteRow>Paginated document/note rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.qstring | nullEchoed search term when present.

Status codes

StatusWhen it happensResponse shape
200Document/note rows were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The query string failed validation.Laravel validation JSON or an explicit message.

Get contact document / note

Returns one contact document/note row when it belongs to the contact from the path and passes the same visibility rules as the list endpoint.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/documents-and-notes/{documentId}
PermissionSame access and visibility rules as the list endpoint.
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with a ContactDocumentNoteRow object.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
dataContactDocumentNoteRowDocument/note payload for the requested row.

Status codes

StatusWhen it happensResponse shape
200The document/note row was returned successfully.{ "data": ContactDocumentNoteRow } or CSV download.
404The contact, document id, or visibility rule check failed.{ "message": "Not found" }

Create contact document / note

Creates a document or note on a contact and optionally uploads attachments, using the same persistence path as the web Documents & notes form.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/contacts/{id}/documents-and-notes
PermissionContact update-level access for the contact type, visibility to the contact, and permission to manage that contact type.
Request encodingapplication/json or multipart/form-data.
Upload rulesSupports up to 10 attached files. Each file must fit within config('constants.document_size_limit').
Success response201 with the created ContactDocumentNoteRow.

Request body

FieldTypeRequiredDescription
headingstringYesDocument/note heading.
descriptionstring | nullNoOptional note body.
is_privatebooleanNoMarks the note as private to its creator.
files[]array<file>NoOptional multipart attachments. Files are uploaded and attached to the created document/note row.

Status codes

StatusWhen it happensResponse shape
201The document/note row was created successfully.{ "message": string, "data": ContactDocumentNoteRow }
403The token user cannot update this contact or cannot manage its contact type.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The payload or uploaded files failed validation.Laravel validation JSON.
500The create transaction failed unexpectedly.{ "message": string }

Update contact document / note

Updates a contact document/note row and optionally appends new attachments.

Endpoint summary

PropertyValue
MethodsPATCH or PUT
Path/api/v1/integration/contacts/{id}/documents-and-notes/{documentId}
PermissionSame write permissions as Create contact document / note.
Request encodingapplication/json or multipart/form-data.
Visibility rulesThe note must belong to the contact from the path and still satisfy the same private/public visibility checks as the show endpoint.
Success response200 with the updated ContactDocumentNoteRow.

Request body

FieldTypeRequiredDescription
headingstringYesUpdated heading.
descriptionstring | nullNoUpdated note body.
is_privatebooleanNoUpdated private/public flag.
files[]array<file>NoOptional additional multipart attachments to append to the note.

Status codes

StatusWhen it happensResponse shape
200The document/note row was updated successfully.{ "message": string, "data": ContactDocumentNoteRow }
403The token user cannot update this contact or cannot manage its contact type.{ "message": "Unauthorized" }
404The contact or document id does not exist in the current business.{ "message": "Not found" }
422The payload or uploaded files failed validation.Laravel validation JSON.
500The update transaction failed unexpectedly.{ "message": string }

Delete contact document / note

Deletes a contact document/note row and its related media links.

Endpoint summary

PropertyValue
MethodDELETE
Path/api/v1/integration/contacts/{id}/documents-and-notes/{documentId}
PermissionSame write permissions as Create contact document / note.
Success response200 with the deleted document id.

Status codes

StatusWhen it happensResponse shape
200The document/note row was deleted successfully.{ "message": string, "data": { "id": integer } }
403The token user cannot update this contact or cannot manage its contact type.{ "message": "Unauthorized" }
404The contact or document id does not exist in the current business.{ "message": "Not found" }
500The delete transaction failed unexpectedly.{ "message": string }

Contact payments history

Lists payment rows for a contact, matching the parent-payment view shown on the web contact Payments tab.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/contacts/{id}/payments
PermissionSame access and visibility rules as Get contact.
Parent-row behaviorOnly parent payments are listed in data.payments; child splits are nested under each parent row.
Summary behaviordata.summary is calculated across all payment rows matching the date filter, including child rows, and is not narrowed by q.
CSV behaviorformat=csv streams all matching parent rows and ignores pagination. Nested child_payments values are JSON-encoded in cells.
Success response200 with payments, summary totals, and paginator metadata.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number.
start_date, end_datestringNoOptional payment date range in Y-m-d. Both must be provided together.
qstringNoMinimum 2 characters when sent. Matches payment reference fields, note, method, cheque/card/bank fields, linked transaction reference numbers, or numeric payment id.
formatstringNojson (default) or csv.

ContactPaymentRow object

FieldTypeDescription
idintegerPayment primary key.
amountnumberPayment amount.
is_returnbooleanWhether the payment is a return payment.
method, method_labelstring | nullStored payment method key and display label.
paid_onstring | nullISO-8601 payment timestamp.
payment_ref_no, transaction_nostring | nullPayment reference fields.
invoice_no, ref_nostring | nullLinked transaction invoice/reference numbers.
transaction_typestring | nullLinked transaction type.
transaction_id, return_parent_idinteger | nullLinked transaction ids when present.
cheque_number, card_transaction_number, bank_account_numberstring | nullMethod-specific reference fields.
child_paymentsarray<object>Child payment splits. Each child row includes id, amount, method, method_label, paid_on, and payment_ref_no.

ContactPaymentSummary object

FieldTypeDescription
countintegerTotal payment rows counted for the summary query.
total_paidnumberTotal paid amount for the summary query.
methods_countintegerNumber of distinct payment methods in the summary query.
latest_paid_onstring | nullMost recent payment timestamp in ISO-8601 format.

Top-level JSON response

FieldTypeDescription
data.contact_idintegerContact id from the path.
data.paymentsarray<ContactPaymentRow>Paginated parent payment rows.
data.summaryContactPaymentSummarySummary block for the applied date filter.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.
meta.start_date, meta.end_date, meta.qstring | nullEchoed filters when present.

Status codes

StatusWhen it happensResponse shape
200Payment rows were returned successfully.JSON { "data": { ... }, "meta": { ... } } or CSV download.
403The token user cannot access this contact.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The query string failed validation, such as incomplete date filters or a 1-character q.Laravel validation JSON or an explicit message.

Record contact payment

Records a payment against a contact due balance using the same payment engine as the web Pay due action.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/contacts/{id}/payments
AuthenticationBearer token or API key + secret required.
Permissionsell.payments for sell/sell_return; purchase.payments for purchase/purchase_return. When due_payment_type is omitted, the controller defaults to sell for customer/both contacts and purchase for supplier contacts.
Visibility rulesSame contact access rules as Get contact.
Request encodingapplication/json
Success response201 with a compact created-payment summary.

Required headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_ACCESS_TOKEN
Content-TypeYesapplication/json
AcceptNoRecommended: application/json.

Request body

FieldTypeRequiredDescription
amountnumberYesPayment amount to post against the contact due balance.
methodstringYesPayment method key. It must exist in the app's enabled payment method set.
paid_onstring | nullNoPayment date/datetime accepted by Laravel's date validator.
due_payment_typestring | nullNosell, purchase, sell_return, or purchase_return.
is_reversebooleanNoMarks the payment as a reverse payment when true.
notestring | nullNoOptional payment note.
account_idinteger | nullNoOptional account id for the payment.
cheque_number, bank_account_number, card_transaction_numberstring | nullNoMethod-specific reference fields.
card_number, card_holder_name, card_type, card_month, card_year, card_securitystring | nullNoCard payment metadata.
transaction_no_1 to transaction_no_7string | nullNoCustom payment-method reference fields.

Success response

FieldTypeDescription
messagestringLocalized payment success message.
data.idintegerCreated payment id.
data.contact_idintegerContact id from the path.
data.payment_ref_nostring | nullGenerated payment reference number.
data.amountnumberStored payment amount.
data.methodstringStored payment method key.
data.paid_onstring | nullISO-8601 payment timestamp.
data.is_advancebooleanWhether the payment was treated as an advance payment.
data.payment_typestring | nullStored payment type returned by the payment engine.

Status codes

StatusWhen it happensResponse shape
201The payment was recorded successfully.{ "message": string, "data": { ... } }
403The token user cannot access the contact or lacks the required payment permission for the selected payment type.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The payload failed validation, the payment method is unknown, or the payment engine rejected the request.Laravel validation JSON or { "message": string, "errors": { ... } }.

Create contact

Creates a new contact in the authenticated business using the same contact pipeline as the web contact form.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/contacts
AuthenticationBearer token or API key + secret required.
PermissionAt least one of supplier.create, customer.create, supplier.view_own, or customer.view_own. The chosen type must also pass the controller's type-specific create permission check.
Subscription ruleReturns 402 when package enforcement is active and the business is not subscribed.
Request encodingapplication/json
Success response201 with the full ContactDetail object.

Required headers

HeaderRequiredDescription
AuthorizationYesBearer YOUR_ACCESS_TOKEN
Content-TypeYesapplication/json
AcceptNoRecommended: application/json.

Core request fields

FieldTypeRequiredDescription
typestringYescustomer, supplier, or both.
namestringYesPrimary contact display name.
mobilestring | nullNoPrimary mobile number. Defaults to an empty string when omitted.
supplier_business_namestring | nullNoBusiness label for supplier/company contacts.
prefix, first_name, middle_name, last_namestring | nullNoOptional person-name fields.
emailstring | nullNoEmail address.
contact_idstring | nullNoOptional business-facing contact code. Duplicate values return 422.
tax_numberstring | nullNoTax number for the contact.
contact_typestring | nullNoindividual or business.
contact_statusstring | nullNoOptional initial status such as active or inactive.
dobstring | nullNoDate of birth in Y-m-d format.

Address and commercial fields

FieldTypeRequiredDescription
pay_term_number, pay_term_typeinteger | string | nullNoOptional supplier pay-term settings. pay_term_type must be days or months.
landline, alternate_numberstring | nullNoAdditional phone numbers.
city, state, countrystring | nullNoAddress summary fields.
address_line_1, address_line_2, zip_codestring | nullNoStreet and zip details.
shipping_addressstring | nullNoShipping address text.
positionstring | nullNoStored latitude/longitude string used by the contact map.
land_mark, street_name, building_number, additional_numberstring | nullNoExtended address fields.
customer_group_idinteger | nullNoCustomer group id for this business.
credit_limitnumber | nullNoCredit limit for the contact.
opening_balancenumber | nullNoOpening balance amount. The contact pipeline will create the related opening balance transaction when needed.
assigned_to_usersarray<integer> | nullNoUser ids assigned to this contact.

Custom and export fields

FieldTypeRequiredDescription
shipping_custom_field_detailsarray | nullNoStructured shipping custom field payload.
custom_field1 to custom_field10string | nullNoBusiness-defined custom contact fields.
is_exportbooleanNoEnables export custom fields when true.
export_custom_field_1 to export_custom_field_6string | nullNoExport-only custom fields stored when is_export is enabled.

Success response

FieldTypeDescription
messagestringLocalized create success message.
dataContactDetailFull contact payload, matching Get contact.

Status codes

StatusWhen it happensResponse shape
201The contact was created successfully.{ "message": string, "data": ContactDetail }
402The business is not subscribed when package enforcement is active.{ "message": string }
403The token user lacks permission to create the requested contact type.{ "message": "Unauthorized" }
422The payload failed validation or the supplied contact_id is already used in the current business.Laravel validation JSON or { "message": string }.
500The create transaction failed unexpectedly.{ "message": "something_went_wrong" }

Update contact

Partially updates an existing contact. The endpoint accepts the same field set as Add contact, but every field is optional.

Endpoint summary

PropertyValue
MethodsPATCH or PUT
Path/api/v1/integration/contacts/{id}
AuthenticationBearer token or API key + secret required.
PermissionAt least one of supplier.update, customer.update, supplier.view_own, or customer.view_own, plus visibility to the contact from the path.
Type ruleThe effective type after the update must still pass the controller's type-specific create permission check.
Subscription ruleReturns 402 when package enforcement is active and the business is not subscribed.
Request encodingapplication/json
Success response200 with the full updated ContactDetail object.

Patch rules

RuleDescription
Accepted fieldsAll request fields documented under Add contact are accepted here as optional patch fields.
Empty bodyAn empty or unusable payload returns 422 with No valid fields to update.
contact_statusUse active or inactive to toggle the same status flag used by the web contact status control.
mobileWhen present, the controller normalizes null to an empty string.
is_exportWhen set to false, export custom fields sent in the same payload are ignored.
Duplicate contact codesUpdating contact_id to a value already used in the same business returns 422.

Success response

FieldTypeDescription
messagestringLocalized update success message.
dataContactDetailFull updated contact payload, matching Get contact.

Status codes

StatusWhen it happensResponse shape
200The contact was updated successfully.{ "message": string, "data": ContactDetail }
402The business is not subscribed when package enforcement is active.{ "message": string }
403The token user cannot update the contact or the effective contact type is not allowed.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The patch payload failed validation, no valid fields were supplied, or the updated contact_id is duplicated.Laravel validation JSON or { "message": string }.
500The update transaction failed unexpectedly.{ "message": "something_went_wrong" }

Delete contact

Deletes a contact when it is safe to do so. This follows the same delete blockers and side effects as the web contact delete action.

Endpoint summary

PropertyValue
MethodDELETE
Path/api/v1/integration/contacts/{id}
AuthenticationBearer token or API key + secret required.
PermissionAt least one of supplier.delete, customer.delete, supplier.view_own, or customer.view_own. For both-type contacts, the delete check must pass for both customer and supplier sides.
Visibility rulesSame contact visibility rules as Get contact.
Success response200 with the deleted contact id.

Delete blockers and side effects

RuleDescription
Existing transactionsIf any transactions row exists for this contact in the current business, the delete is blocked with 422.
Default contactDefault contacts such as the walk-in customer cannot be deleted.
CRM-linked usersOn success, users linked by crm_contact_id have allow_login disabled.
Delete behaviorThe controller records activity, soft-deletes the contact, and dispatches the same contact modified event used elsewhere in the app.

Status codes

StatusWhen it happensResponse shape
200The contact was deleted successfully.{ "message": string, "data": { "id": integer } }
403The token user cannot delete the contact or lacks the required type-specific delete permission.{ "message": "Unauthorized" }
404The contact id does not exist in the current business.{ "message": "Not found" }
422The contact still has transactions or is the default contact.{ "message": string }
500The delete transaction failed unexpectedly.{ "message": "something_went_wrong" }

List customer groups

Lists customer groups for the authenticated business, using the same rows shown in the web Customer Groups screen.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/customer-groups
AuthenticationBearer token or API key + secret required.
Permissioncustomer.view
CSV behaviorformat=csv streams all matching rows with a UTF-8 BOM and ignores pagination.
Success response200 with paginated CustomerGroup rows.

Query parameters

ParameterTypeRequiredDescription
per_pageintegerNoResults per page from 1 to 100. Defaults to 20.
pageintegerNoPagination page number.
qstringNoMinimum 2 characters when sent. Matches the customer group name, linked selling price group name, or numeric customer group id.
formatstringNojson (default) or csv.

CustomerGroup object

FieldTypeDescription
idintegerCustomer group primary key.
namestringCustomer group name.
price_calculation_typestringpercentage or selling_price_group.
amountnumberStored percentage amount or zero when the customer group is driven by a selling price group.
selling_price_group_idinteger | nullLinked selling price group id when price_calculation_type is selling_price_group.
selling_price_group_namestring | nullLinked selling price group name when applicable.
created_byinteger | nullCreator user id.
created_at, updated_atstring | nullISO-8601 timestamps.

Top-level JSON response

FieldTypeDescription
dataarray<CustomerGroup>Paginated customer group rows.
meta.current_page, meta.last_page, meta.per_page, meta.totalintegerPagination metadata.

Status codes

StatusWhen it happensResponse shape
200Customer groups were returned successfully.JSON { "data": [...], "meta": { ... } } or CSV download.
403The token user lacks customer.view.{ "message": "Unauthorized" }
422The query string failed validation, such as a 1-character q.Laravel validation JSON or an explicit message.

Get customer group

Returns one customer group for the authenticated business.

Endpoint summary

PropertyValue
MethodGET
Path/api/v1/integration/customer-groups/{id}
Permissioncustomer.view
CSV behaviorformat=csv streams one row with a data_json column.
Success response200 with a CustomerGroup object.

Query parameters

ParameterTypeRequiredDescription
formatstringNojson (default) or csv.

Success response

FieldTypeDescription
dataCustomerGroupCustomer group payload for the requested row.

Status codes

StatusWhen it happensResponse shape
200The customer group was returned successfully.{ "data": CustomerGroup } or CSV download.
403The token user lacks customer.view.{ "message": "Unauthorized" }
404The customer group id does not exist in the current business.{ "message": "Not found" }

Create customer group

Creates a customer group for the authenticated business.

Endpoint summary

PropertyValue
MethodPOST
Path/api/v1/integration/customer-groups
Permissioncustomer.create
Request encodingapplication/json
Success response201 with the created CustomerGroup object.

Request body

FieldTypeRequiredDescription
namestringYesCustomer group name.
price_calculation_typestringYespercentage or selling_price_group.
amountnumber | nullNoPercentage amount. The controller stores zero when omitted.
selling_price_group_idinteger | nullConditionalRequired when price_calculation_type is selling_price_group. The id must belong to the current business.

Status codes

StatusWhen it happensResponse shape
201The customer group was created successfully.{ "message": string, "data": CustomerGroup }
403The token user lacks customer.create.{ "message": "Unauthorized" }
422The payload failed validation or a selling price group was required but not provided.Laravel validation JSON or { "message": string }.
500The create transaction failed unexpectedly.{ "message": string }

Update customer group

Updates an existing customer group. The endpoint accepts partial updates and applies the same price-calculation rules as the create flow.

Endpoint summary

PropertyValue
MethodsPATCH or PUT
Path/api/v1/integration/customer-groups/{id}
Permissioncustomer.update
Request encodingapplication/json
Success response200 with the updated CustomerGroup object.

Request body

FieldTypeRequiredDescription
namestringNoUpdated customer group name.
price_calculation_typestringNopercentage or selling_price_group.
amountnumber | nullNoUpdated percentage amount.
selling_price_group_idinteger | nullConditionalRequired when the effective calculation type is selling_price_group. The id must belong to the current business.

Status codes

StatusWhen it happensResponse shape
200The customer group was updated successfully.{ "message": string, "data": CustomerGroup }
403The token user lacks customer.update.{ "message": "Unauthorized" }
404The customer group id does not exist in the current business.{ "message": "Not found" }
422The payload failed validation or a required selling price group id is missing.Laravel validation JSON or { "message": string }.
500The update transaction failed unexpectedly.{ "message": string }

Delete customer group

Deletes a customer group in the authenticated business.

Endpoint summary

PropertyValue
MethodDELETE
Path/api/v1/integration/customer-groups/{id}
Permissioncustomer.delete
Success response200 with the deleted customer group id.

Status codes

StatusWhen it happensResponse shape
200The customer group was deleted successfully.{ "message": string, "data": { "id": integer } }
403The token user lacks customer.delete.{ "message": "Unauthorized" }
404The customer group id does not exist in the current business.{ "message": "Not found" }
500The delete transaction failed unexpectedly.{ "message": string }