Skip to main content

GraphQL API

CXTMS provides a comprehensive GraphQL API for interacting with the system. The API supports queries, mutations, and subscriptions for real-time updates.

Base URL

https://api.cargoxplorer.com/graphql

Authentication

All GraphQL requests require authentication via Bearer token in the Authorization header:

Authorization: Bearer <your-access-token>

Common Patterns

Entity Fields Lookup

The entityFields query accepts an optional entityName argument. Matching is case-insensitive and uses SQL ILIKE, so callers can pass an exact entity name or an ILIKE pattern.

query {
entityFields(organizationId: 1, entityName: "Order", take: 1000) {
items {
name
fieldDefinition
}
}
}

Use search to match field names by substring.

Pagination

List queries support offset-based pagination with the following parameters:

ParameterTypeDescription
skipIntNumber of items to skip
takeIntNumber of items to return (default: 20, max: 100)

Filtering

Most list queries support filtering via the filter parameter using Lucene query syntax:

query {
getAttachments(organizationId: 1, filter: "attachmentType:Picture") {
items {
attachmentId
fileName
}
}
}

Sorting

Use the orderBy parameter to sort results:

query {
getAttachments(organizationId: 1, orderBy: "created desc") {
items {
attachmentId
fileName
}
}
}

Full-text search is available via the search parameter. For orders and orderGroupBy, order quick search also includes linked commodity InventoryItem values (sku, productName, description, modelNumber, and customValues) so warehouse item identifiers can find the containing orders.

query {
getAttachments(organizationId: 1, search: "invoice") {
items {
attachmentId
fileName
}
}
}

Contact Import Mutation

importContacts

importContacts imports contacts from an uploaded CSV, JSON, or XLSX file. The importer now processes rows as dynamic dictionaries, so inbound keys are matched case-insensitively and can use either exact entity field names or columnMappings.

mutation ImportContacts($input: ImportContactsInput!) {
importContacts(input: $input) {
added
updated
errors
}
}
{
"input": {
"organizationId": 1,
"fileUploadUrl": "uploads/imports/contacts.xlsx",
"contactType": "Customer",
"columnMappings": {
"Name": "Company Name",
"EmailAddress": "Email",
"Division.DivisionName": "Division"
}
}
}

Import behavior

  • Existing contacts are matched by ContactId first. Primary keys (ContactId, OrganizationId) are not applied as update fields.
  • Empty strings and nulls are skipped on updates so blank import cells do not wipe existing values.
  • contactType is an optional mutation-level default for new contacts. A row-level ContactType value wins when present. If neither is supplied, new contacts default to Contact.
  • Nested division data can resolve DivisionId by division.divisionName / Division.DivisionName within the organization.
  • New rows without Name are skipped rather than creating invalid contacts.

Order Resolvers

getCommoditiesWithRelatedOrder

Returns the flat list of leaf commodities linked to shipments of a specified order type within an order's commodity hierarchy. Available as a field on the Order type.

query {
getOrders(organizationId: 1, take: 1) {
items {
orderId
getCommoditiesWithRelatedOrder(orderType: "ParcelShipment", filter: "weight>0") {
commodityId
description
pieces
weight
}
}
}
}

Arguments

ParameterTypeRequiredDescription
orderTypeString!YesThe related order type to filter by (e.g., ParcelShipment, PickupOrder). Uses the OrderTypes enum values (case-insensitive).
filterStringNoLucene filter to apply on the returned commodities

Behavior

  • Traverses the order's commodity hierarchy via OrderCommodityHierarchyView
  • Finds commodities linked to related orders of the specified type
  • Returns only leaf commodities — excludes wrapper/consolidated commodities whose children are also linked
  • Supports projection and filtering

getContactAddress (Order)

Returns a contactAddress from an order custom-value field that stores a contact-address id. Available as a field on the Order type.

query {
getOrders(organizationId: 1, take: 1) {
items {
orderId
getContactAddress(idPropertyName: "pickupContactAddressId") {
contactAddressId
name
addressLine
cityName
}
}
}
}
ParameterTypeRequiredDescription
idPropertyNameString!YesCustom-values key on the order containing the ContactAddressId.

The resolver returns an empty result when the custom value is missing and enforces organization scope when loading the address.

Tracking Event Resolvers

getLastTrackingEvent (Order)

Returns the most recent tracking event for an order. Available as a field on the Order type.

The resolver uses a DataLoader to batch all lastTrackingEvent requests within a single GraphQL operation into one or two database round-trips, eliminating N+1 queries when fetching tracking events for multiple orders at once.

query {
getOrders(organizationId: 1, take: 10) {
items {
orderId
getLastTrackingEvent(eventDefinitionName: "Departed") {
trackingEventId
eventDate
description
}
}
}
}

Arguments

ParameterTypeRequiredDescription
eventDefinitionNameStringNoFilter to events of a specific definition name. When omitted, the latest event of any type is returned.
orderByStringNoControls which event is selected as the "winner". Use the standard -field prefix convention: omit or pass a value starting with - for DESC (latest / most recent event); pass a value without a - prefix for ASC (earliest / "first seen" event). Defaults to DESC when omitted.

Sort Order

The winning event is determined using the canonical two-key ordering:

orderBy directionKey 1Key 2 (tie-breaker)
DESC (default — omitted or starts with -)COALESCE(EventDate, Created) DESCTrackingEventId DESC
ASC (any non-prefixed value)COALESCE(EventDate, Created) ASCTrackingEventId ASC

EventDate is used when set; falls back to Created so events that only have a creation timestamp are still included in the ordering.

DataLoader Batching

All getLastTrackingEvent calls sharing the same (OrganizationId, EventDefinitionName, OrderBy) are grouped into a single batch query. The DataLoader:

  1. Fetches only the minimum columns needed (TrackingEventId, sort date) to determine the winner per order
  2. Re-fetches the winning events through AutoMapper projection, materializing only the fields requested in the GraphQL selection set
note

Because OrderBy is part of the batch key, a query that requests both ASC and DESC lastTrackingEvent in the same operation will issue two separate batch queries — one per direction.


getCommodityLastTrackingEvent (Commodity)

Returns the most recent tracking event for a commodity. Available as a field on the Commodity type. Mirrors getLastTrackingEvent but walks the Commodity ↔ TrackingEvent relationship.

query {
getCommodities(organizationId: 1, take: 10) {
items {
commodityId
description
getCommodityLastTrackingEvent(eventDefinitionName: "Arrived") {
trackingEventId
eventDate
description
}
}
}
}

Arguments

ParameterTypeRequiredDescription
eventDefinitionNameStringNoFilter to events of a specific definition name.
orderByStringNoControls which event is selected as the "winner". Omit or pass a --prefixed value for DESC (latest); any non-prefixed value for ASC (earliest). Defaults to DESC when omitted.

Sort Order

Identical to the Order resolver — canonical two-key ordering:

orderBy directionKey 1Key 2 (tie-breaker)
DESC (default)COALESCE(EventDate, Created) DESCTrackingEventId DESC
ASCCOALESCE(EventDate, Created) ASCTrackingEventId ASC

DataLoader Batching

All getCommodityLastTrackingEvent calls with the same (OrganizationId, EventDefinitionName, OrderBy) are batched together. The OrganizationId used for batching is taken from the parent Commodity.OrganizationId, which is always projected via AutoMapperProjectTo to ensure it is available.


getWeightTotal (Commodity)

Returns a commodity's total weight in the requested unit. If weightUnit is omitted, the resolver uses the commodity's own weightUnit.

query {
getCommodities(organizationId: 1, take: 10) {
items {
commodityId
description
weightUnit
getWeightTotal(weightUnit: "Lbs")
}
}
}
ParameterTypeRequiredDescription
weightUnitStringNoTarget weight unit. Defaults to the parent commodity's weightUnit.

Entity Field Metadata

entityFields results include priority, the module-level precedence used when multiple app modules define the same field name for the same entity. When querying a specific entityName, inactive fields are filtered out and duplicate field definitions resolve to the highest-priority active field.

query {
entityFields(organizationId: 1, entityName: "ParcelShipment") {
items {
name
isCustomField
isInactive
priority
fieldDefinition
}
}
}

Available APIs