Data Mart API - 2026-09-28
In this release:
- Customers: field values, new fields, notes, related-data lookups and reimport.
- Locations: nullable GLN and historical data refresh.
- Contacts: date of birth, Employee lookup and historical data availability.
- Branches: branch name and Employee lookup.
- SalesOrders: removed fields and replacement lookups.
- Employees: employee fields, related-data lookups and the attributes identifier.
- Customer and supplier IDs on lines: new
customerIdandsupplierIdfields on sales order, document and payment lines. - GeneralLedgerBalances: new
deletedDatabaseRecordfield andincludeDeletedargument; removed balances no longer returned by default. - CustomerPayments and CustomerPaymentLines: cash-document inclusion remains disabled.
- ARHistoricalData: remains disabled in Production.
Customers
Breaking change: credit verification and statement type values
The Customer fields creditVerification and statementType previously returned the raw ERP single-character codes. They now return the same values as the Visma.net REST API /customer endpoint:
| Field | Before | After |
|---|---|---|
creditVerification | N / C / D / B | Disabled / CreditLimit / DaysPastDue / LimitAndDaysPastDue |
statementType | O / B | OpenItem / BalanceBroughtForward |
If your integration compares or filters on the short codes, update it to the new values.
creditVerification also changes from String! to String (nullable). An unknown or missing source code returns null for either field instead of a raw code.
New fields
The Customer type now exposes additional customer-owned fields and the customer attributes key:
attributesId: String- identifies the customer's attributes. This is an identifier only; attribute values are not yet available through a DataMart query.note: String- the customer's own note text. It can benullor an empty string; line breaks and Unicode are preserved.
And the customer's invoicing and debt collection settings, matching the Visma.net REST API /customer endpoint:
printDunningLetters: Boolean- dunning letters are printed for the customer.sendDunningLettersViaEmail: Boolean- dunning letters are sent to the customer by email.invoiceToDefaultLocation: Boolean- invoices are addressed to the customer's default location.excludeDebtCollection: Boolean- the customer is excluded from debt collection (Autocollect).defaultPaymentMethodId: String- the identifier of the customer's default payment method; links to thepaymentMethodsquery.
All added fields are nullable in the schema. See the ETL source-value handling for defaults applied during synchronization.
Customer notes
Select note only after the updated API and Customers ETL are deployed in your environment.
query {
customers(pageSize: 100) {
customerId
customerNumber
note
}
}
The note text is separate from attributesId, which is an identifier, not an attributes payload. See the ETL note synchronization for updates, clearing notes and historical data.
For comparison with the ERP API, retrieve a specific customer's note using GET /v1/customer/{customerCd}/note. The ERP customer list response does not provide this value.
Customer lookups
Use the customers query for customer-owned identifiers, then resolve related records through their corresponding queries. Match companyId in every lookup.
Billing and main address or contact
Billing and main address/contact IDs already exist on Customer. Each lookup below also matches the address or contact companyId to Customer.companyId.
| Customer value | Query | Match within the same company |
|---|---|---|
billAddressId | addresses | Address.addressId = Customer.billAddressId and Address.bAccountId = Customer.customerId |
billContactId | contacts | Contact.contactId = Customer.billContactId and Contact.bAccountId = Customer.customerId |
mainAddressId | addresses | Address.addressId = Customer.mainAddressId and Address.bAccountId = Customer.customerId |
mainContactId | contacts | Contact.contactId = Customer.mainContactId and Contact.bAccountId = Customer.customerId |
Default location and delivery details
Find the customer's default record through locations, matching all three keys:
Location.companyId=Customer.companyIdLocation.bAccountId=Customer.customerIdLocation.locationId=Customer.mainLocationId
Customer does not expose deliveryAddressId, deliveryContactId, or priceClassId. Read these values from the matched Location:
| Information | Location field | Follow-up lookup |
|---|---|---|
| Delivery address | addressId | Match Location.addressId = Address.addressId, Location.bAccountId = Address.bAccountId, and Location.companyId = Address.companyId in addresses. |
| Delivery contact | contactId | Match Location.contactId = Contact.contactId, Location.bAccountId = Contact.bAccountId, and Location.companyId = Contact.companyId in contacts. |
| Price class | priceClassId | Keep the identifier; a price class description lookup is not yet available. |
| Global Location Number (GLN) | gln | Read the nullable value directly from Location; see Locations for historical data refresh. |
If an ID is null or the related record is unavailable, the lookup may return no matching record. For main, billing or delivery Attention, resolve the appropriate contact first and read Contact.salutation.
Payment methods and other lookup availability
Customer defaultPaymentMethodId and Location paymentMethodId represent different source settings. Use the Customer field for the customer's default payment method and the Location field for the matched location's payment method. Resolve either identifier through paymentMethods, matching both companyId and paymentMethodId. The ETL release notes identify the corresponding ERP source fields.
Price class descriptions and customer-to-salesperson relationships are not yet exposed by dedicated DataMart queries. Location.priceClassId identifies the price class, but there is no PriceClasses description lookup. The existing salesPersons query does not supply the missing customer-to-salesperson relationship.
Customer reimport
Existing records require a full Customer reimport after backfill completion. Follow the ETL backfill and reimport instructions, including the release-completion notification.
Locations
Global Location Number (GLN)
Location.gln is a nullable field available through the locations query. A null GLN is a valid value. Use the Customer default-location keys to find the appropriate Location for a customer.
Historical data and reimport
Previously imported Locations need a refresh after their backfill is confirmed complete. Follow the ETL Locations reimport instructions.
Contacts
Date of birth
The nullable Contact.dateOfBirth field uses the YYYY-MM-DD calendar format without a time or timezone. A missing source date returns null. See the Contact field reference for the schema definition.
Select the field where the updated Contacts API and ETL are deployed. Historical coverage also depends on the separate backfill. See the ETL Contacts backfill and incremental-fetch guidance before refreshing previously imported data.
Employee lookup
Date of birth belongs to Contacts and is not duplicated on Employees. Use the Employee-to-Contact lookup, matching the company and account scope as well as the contact ID, then read Contact.dateOfBirth.
Branches
Branch name
The branches query exposes nullable branchName, the name of the branch's associated business account. The existing branchCode is unchanged. See the Branch reference for the complete field list.
To find an employee's branch, match Employee.companyId = Branch.companyId and Employee.parentBAccountId = Branch.bAccountId, then read branchCode and branchName. A missing reference or unavailable record may produce no match.
Use the field where the updated API and Branches ETL are deployed. For existing documents, follow the ETL Branches backfill guidance.
SalesOrders
Breaking change: field removal
The updated GraphQL schema removes 11 of the previously announced SalesOrder fields. soShippingContactName remains available. Update queries, mappings and reports that depend on the removed fields. A GraphQL query that selects a removed field will fail validation.
Use the following collections to look up the values. In every lookup, match the collection's companyId to SalesOrder.companyId in addition to the keys shown below. Field names on the left of each key match belong to SalesOrder; names on the right belong to the target collection.
| Removed SalesOrder field | Collection / GraphQL query | Value to read | Matching keys |
|---|---|---|---|
soBillingAddressCountryName | Countries / countries | description | soBillingAddressCountryId = countryId |
soShippingAddressCountryName | Countries / countries | description | soShippingAddressCountryId = countryId |
soBillingAddressCountyName | States / states | name | soBillingAddressCountryId = countryId and soBillingAddressCountyId = stateId |
soShippingAddressCountyName | States / states | name | soShippingAddressCountryId = countryId and soShippingAddressCountyId = stateId |
customerVatZoneDescription | VatZones / vatZones | description | customerVatZoneId = vatZoneId |
customerVatZoneDefaultVatCategory | VatZones / vatZones | defaultVatCategoryId | customerVatZoneId = vatZoneId |
creditTermsDescription | Terms / terms | description | creditTermsId = termsId |
salesPersonDescription | SalesPersons / salesPersons | name | salesPersonId = salesPersonId |
locationName | Locations / locations | name | customerId = bAccountId and locationId = locationId |
customerNumber | Customers / customers | customerNumber | customerId = customerId |
ownerName | Employees / employees | employeeName | ownerId = employeeUserId |
These lookups return the current master-data values. A missing key or unavailable record may produce no match. Despite its former name, customerVatZoneDefaultVatCategory contained the category ID, so read VatZone.defaultVatCategoryId, not a category description.
For additional category details, use VatCategories / vatCategories, matching VatZone.defaultVatCategoryId to VatCategory.vatCategoryId within the same companyId.
soShippingContactName continues to come from the order's SOContact shipping-contact snapshot, including order-specific values. Keep reading it directly from SalesOrder together with soShippingContactId; no Contacts lookup is needed.
For ownerName, use the Employees owner lookup where the updated API and ETL are deployed.
The corresponding SalesOrder IDs and codes remain available. The retained fobPointDescription, shippingTermsDescription, shippingZoneDescription, transactionTypeDescription and locationCountryId fields are unchanged.
Use the SalesOrder reference for the current field list.
SalesOrder reimport
After updating queries and mappings, follow the ETL SalesOrder reimport instructions.
Employees
Employee fields
The employees query exposes employee identity, work details and references to related data. Identify each employee by companyId and employeeId together. employeeUserId is a nullable user UUID, not the employee's numeric key.
| Fields | Information |
|---|---|
employeeNumber, employeeName | Employee number and name |
status | Active, OnHold, Inactive, HoldPayments or OneTime |
lastModifiedDateTime, timeStamp | ERP modification time and timestamp |
employeeUserId | Associated user identifier, when available |
parentBAccountId, mainContactId, mainAddressId | References to branch, contact and address data |
department, calendarId, employeeClass.id | Department, calendar and employee-class identifiers; use the exact calendarId casing |
employeeLogin | Username and full name separated by - when both exist; otherwise null |
workGroupDescription | List of associated workgroup descriptions |
deletedDatabaseRecord | Soft-deletion state |
Standard DataMart fields id, createdInDataMartDateTime and updatedInDataMartDateTime are also included. See the Employee reference for types and nullability, and the ETL Employees section for import and availability requirements.
Related master data
Match companyId for every record in every lookup. A null or missing reference, or a reference with no same-company match, produces no matching record. Use these fields where the updated API and Employees ETL are deployed in your environment. Contact date of birth and branch names stay in their owning collections.
Resolve the Employee's Address first, then use that Address for the Country and State or county rows.
| Data | Match within the same company | Read |
|---|---|---|
| Branch | Employee.parentBAccountId = Branch.bAccountId | Branch.branchCode, Branch.branchName |
| Contact | Employee.mainContactId = Contact.contactId and Employee.parentBAccountId = Contact.bAccountId | Contact fields, including Contact.dateOfBirth |
| Address | Employee.mainAddressId = Address.addressId and Employee.parentBAccountId = Address.bAccountId | Address fields |
| Country | Address.countryId = Country.countryId | Country.description |
| State or county | Address.countryId = State.countryId and Address.state = State.stateId | State.name |
| Employee class | Employee.employeeClass.id = SupplierClass.supplierClassId | SupplierClass.description |
See the Contacts section for the date-of-birth format, nullability and availability details.
SalesOrder owner lookup
Query employees and read Employee.employeeName for the SalesOrder owner, matching Employee.companyId = SalesOrder.companyId and Employee.employeeUserId = SalesOrder.ownerId. Use this lookup where the updated API and ETL are deployed. The contract does not include a standalone BAccounts collection.
Employee.employeeUserId is nullable. If SalesOrder.ownerId is null, or no Employee in the same company has that employeeUserId, there is no owner match. Employee.employeeId is the Employee identity and is not the key for this lookup.
Attributes identifier
The updated Employee schema includes nullable noteId (UUID) for future attribute lookup within the same company. This is separate from employeeId and employeeUserId; employeeUserId remains the SalesOrder owner lookup key. Attribute values are not yet available in DataMart. Select noteId only where the updated API is deployed; a missing value is returned as null. See the ETL Employees section for regular-import and deployment requirements.
Customer and supplier IDs on lines
New fields
Sales order, document and payment lines now include the customer or supplier of their parent document, so a line can be related to its customer or supplier without first looking up the parent document.
| Query | Type | New field | Parent document |
|---|---|---|---|
salesOrderLinesV2 | SalesOrderLine | customerId: Int | SalesOrder |
customerDocumentLines | CustomerDocumentLine | customerId: Int! | CustomerDocument |
supplierDocumentLines | SupplierDocumentLine | supplierId: Int! | SupplierDocument |
customerPaymentLines | CustomerPaymentLine | customerId: Int! | CustomerPayment |
supplierPaymentLines | SupplierPaymentLine | supplierId: Int! | SupplierPayment |
The deprecated salesOrderLines, salesOrderLineById, apInvoiceLines and apPaymentLines queries return the same fields on SOLine, APInvoiceLine and APPaymentLine.
SalesOrderLine.customerId is nullable: null means ERP has no customer on the line. The other four fields are non-null.
To read customer or supplier details, match customerId to Customer.customerId in customers, or supplierId to Supplier.supplierId in suppliers, within the same companyId. The fields are returned on each line; the line queries do not add a filter argument for them.
Historical data and reimport
Existing lines receive the values through a historical backfill. The backfill does not advance updatedInDataMartDateTime. An incremental fetch based on this timestamp will therefore not return every backfilled line. Refresh previously imported lines once the backfill is confirmed complete in your environment. See the ETL line backfill.
Until a line has been updated, the non-null fields return 0 and SalesOrderLine.customerId returns null. Treat 0 as "not yet available", not as an ID, and rely on a null sales order line customer only after the backfill is confirmed complete. Lines deleted in ERP before the backfill ran are not updated, so with includeDeleted: true they keep returning 0, or null for sales order lines.
GeneralLedgerBalances
Deleted balances
The GeneralLedgerBalance type now includes deletedDatabaseRecord: Boolean!, set to true when the balance no longer exists in ERP.
All general ledger balance list, period and forward queries take a new includeDeleted: Boolean! = false argument: generalLedgerBalances, generalLedgerBalancesV2, generalLedgerBalancesByPeriod, generalLedgerBalancesByPeriodV2, generalLedgerBalancesForwardByGroup and generalLedgerBalancesForwardByGroupV2. By default, deleted balances are not returned. generalLedgerBalanceById returns the balance regardless; read deletedDatabaseRecord.
Period and forward results
generalLedgerBalancesByPeriod and generalLedgerBalancesByPeriodV2 now return the latest balance at or before the requested period that still exists in ERP. Previously, a balance that ERP had removed in a later period could be returned instead, typically with lastModifiedDateTime set to 0001-01-01T00:00:00Z. For the affected account and subaccount combinations, the returned balance and its period can change.
The forward queries no longer carry a removed balance forward into later periods. When a balance is removed, its group is returned again in an incremental fetch with the corrected timeline.
Incremental fetching
A removed balance receives a new updatedInDataMartDateTime. To remove your own copies, fetch with includeDeleted: true and check deletedDatabaseRecord. Without includeDeleted: true, removed balances stop appearing in the results. See the ETL GeneralLedgerBalances section for how removals are detected.
CustomerPayments and CustomerPaymentLines
Cash-document inclusion is disabled while testing is in progress. For customerPayments and customerPaymentLines, omit includeCashDocumentTypes or set it to false. Using true is not available until testing is complete and the feature is enabled.
ARHistoricalData
AR history is disabled in Production while testing is in progress. The arHistoricalData query and ARHistoricalData type remain documented for reference, but the ETL is not available for Production use.