Table of Contents


IRIS App SDK Reference

Overview

The Magentrix SDK (@magentrix-corp/magentrix-sdk) is the primary way IRIS Apps read and write data. It wraps the REST API V3 and handles authentication, session management, token refresh, and response parsing automatically. For the vast majority of data operations, you will never need to call the REST API directly.

The SDK supports both ESM and CommonJS module formats and ships with full TypeScript type definitions.


Installation

npm install @magentrix-corp/magentrix-sdk

Configuration

The SDK is initialised through the MagentrixConfig interface and a MagentrixClient instance.

import { MagentrixClient, type MagentrixConfig } from '@magentrix-corp/magentrix-sdk'

const config: MagentrixConfig = {
	baseUrl: import.meta.env.VITE_SITE_URL,
	refreshToken: import.meta.env.VITE_REFRESH_TOKEN,
	isDevMode: import.meta.env.DEV
}

const dataService = new MagentrixClient(config)
PropertyTypeRequiredDescription
baseUrlstringYesBase URL of your Magentrix instance (e.g. https://yourcompany.magentrix.com).
isDevModebooleanNoEnables token-based authentication. Default: false. Set to true for local development.
refreshTokenstringNoAPI key used as the refresh token. Required when isDevMode is true.
💡 Best practice: Always read credentials from environment variables (import.meta.env) rather than hardcoding them. Never commit VITE_REFRESH_TOKEN to version control.

Authentication Modes

The SDK operates in one of two authentication modes, controlled by the isDevMode flag.

Development Mode (isDevMode: true)

Uses Bearer token authentication. The SDK automatically refreshes the access token 30 seconds before it expires and validates the session on every API call. A refreshToken is required in the configuration. Use this mode for local development.

const config: MagentrixConfig = {
	baseUrl: 'https://yourcompany.magentrix.com',
	refreshToken: '<your-api-key>',
	isDevMode: true
}

Production Mode (isDevMode: false)

Uses cookie-based authentication via ASP.NET session cookies. No Bearer tokens are sent. When the session expires, the SDK automatically redirects the user to the login page. This is the default mode and requires no refreshToken. Use this mode for apps deployed inside the Magentrix portal.

const config: MagentrixConfig = {
	baseUrl: 'https://yourcompany.magentrix.com',
	isDevMode: false
}

Vue 3 Composable

For Vue 3 components, use the useMagentrixSdk composable instead of instantiating MagentrixClient directly. The composable provides a singleton instance, so the same session is shared across all components.

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { MagentrixError, type MagentrixConfig } from '@magentrix-corp/magentrix-sdk'
import { useMagentrixSdk } from '@magentrix-corp/magentrix-sdk/vue'

const config: MagentrixConfig = {
	baseUrl: import.meta.env.VITE_SITE_URL,
	refreshToken: import.meta.env.VITE_REFRESH_TOKEN,
	isDevMode: import.meta.env.DEV
}

const dataService = useMagentrixSdk().getInstance(config)
const accounts = ref<any[]>([])
const loading = ref(false)
const error = ref<string | null>(null)

async function loadAccounts(): Promise<void> {
	loading.value = true
	error.value = null

	try {
		const result = await dataService.query('SELECT Id, Name FROM Account')
		accounts.value = result.data
	} catch (err) {
		if (err instanceof MagentrixError) {
			error.value = err.message
		}
	} finally {
		loading.value = false
	}
}

onMounted(() => {
	loadAccounts()
})
</script>

<template>
	<div>
		<div v-if="loading">Loading...</div>
		<div v-else-if="error">Error: {{ error }}</div>
		<ul v-else>
			<li v-for="account in accounts" :key="account.Id">
				{{ account.Name }}
			</li>
		</ul>
	</div>
</template>
⚠️ Warning:useMagentrixSdk() is a Vue composable. It must be called synchronously during component setup — never inside an async function, a lifecycle hook body, or a conditional block.
⚠️ Warning: Import RequestMethod from @magentrix-corp/magentrix-sdk (the main package), not from @magentrix-corp/magentrix-sdk/vue. Importing it from the Vue subpath will cause a runtime error.

API Method Reference

Method & SignatureReturn TypeDescription
create(entityName: string, data: any | any[])Promise<any>Creates one or more records. Pass an array for batch operations.
createSession(refreshToken?: string)Promise<SessionInfo>Creates a new session manually. Handled automatically in normal usage.
delete(entityName: string, id: string, permanent?: boolean)Promise<any>Deletes a record by ID. Soft delete by default; pass true for permanent.
deleteMany(entityName: string, ids: string[], permanent?: boolean)Promise<any>Deletes multiple records by ID in a single API call.
edit(entityName: string, data: any | any[])Promise<any>Updates one or more records. Each object must include an Id field.
execute(path: string, model?: any, method?: RequestMethod)Promise<any>Calls a custom controller action. Use only when SDK CRUD methods are insufficient.
getUserInfo()Promise<UserInfo>Returns the currently authenticated user's profile and role information.
query(query: string)Promise<any>Executes a MEQL query string. Returns { data: [...] } for standard queries.
retrieve(id: string)Promise<any>Retrieves a single record by ID. Returns { record: { ... } }.
upsert(entityName: string, data: any | any[])Promise<any>Creates if no Id provided, updates if Id exists.

Method Details

query(query: string)

Executes a MEQL query and returns a data property. For standard queries, data is an array of record objects. For aggregate queries, data is an object with the aggregate field names as properties.

💡 Note: For full MEQL syntax including operators, date functions, aggregates, and pagination, see the MEQL Reference.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'

// Standard query — returns array of records
try {
	const result = await dataService.query(
		'SELECT Id, Name, Email FROM Contact WHERE IsActive = true'
	)
	const contacts = result.data
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Query failed:', error.message)
	}
}

// Aggregate query — returns object with aggregate values
try {
	const result = await dataService.query(
		'SELECT COUNT(Id) as Total, SUM(AnnualRevenue) as Revenue FROM Account'
	)
	console.log('Total:', result.data.Total)
	console.log('Revenue:', result.data.Revenue)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Query failed:', error.message)
	}
}

// Sorted and paginated
try {
	const result = await dataService.query(
		'SELECT Id, Name FROM Account ORDER BY Name ASC LIMIT 25, 0'
	)
	const accounts = result.data
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Query failed:', error.message)
	}
}

retrieve(id: string)

Retrieves a single record by its ID. Returns an object with a record property containing the full record data with all fields accessible to the current user.

⚠️ Warning: The return shape is { record: { ... } } — not the record itself. Always access fields via result.record.FieldName, not result.FieldName.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'

try {
	const result = await dataService.retrieve('00I00000000003x0001')
	const account = result.record

	console.log('Name:', account.Name)
	console.log('Status:', account.Status)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Retrieve failed:', error.message)
	}
}

// Destructured form
try {
	const { record } = await dataService.retrieve('00300000000001A0001')
	console.log('Contact:', record.Name)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Retrieve failed:', error.message)
	}
}

create(entityName: string, data: any | any[])

Creates one or more records. Pass a single object for a single record, or an array for batch creation. Batch creation executes as a single optimised API call.

import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'

// Single record
try {
	const newAccount = await dataService.create('Account', {
		Name: 'Acme Corporation',
		Email: 'contact@acme.com',
		Status: 'Active'
	})
	console.log('Created ID:', newAccount.Id)
} catch (error) {
	if (error instanceof DatabaseError) {
		error.getErrors().forEach(err => {
			console.error(`${err.fieldName}: ${err.message}`)
		})
	} else if (error instanceof MagentrixError) {
		console.error('Create failed:', error.message)
	}
}

// Batch creation — single API call
try {
	const newAccounts = await dataService.create('Account', [
		{ Name: 'Account 1', Status: 'Active' },
		{ Name: 'Account 2', Status: 'Active' },
		{ Name: 'Account 3', Status: 'Active' }
	])
	console.log('Created IDs:', newAccounts.map(a => a.Id))
} catch (error) {
	if (error instanceof DatabaseError) {
		error.getErrors().forEach(err => {
			console.error(`${err.fieldName}: ${err.message}`)
		})
	} else if (error instanceof MagentrixError) {
		console.error('Batch create failed:', error.message)
	}
}
⚠️ Warning: Never use Promise.all with individual create() calls for multiple records. This fires one API request per record. Always pass an array to create() for a single optimised batch call.

edit(entityName: string, data: any | any[])

Updates one or more existing records. Every object in the payload must include an Id field. Only the fields provided are updated — omitted fields are left unchanged.

import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'

// Single record
try {
	await dataService.edit('Account', {
		Id: '00I00000000003x0001',
		Name: 'Updated Name',
		Status: 'Inactive'
	})
} catch (error) {
	if (error instanceof DatabaseError) {
		error.getErrors().forEach(err => {
			console.error(`${err.fieldName}: ${err.message}`)
		})
	} else if (error instanceof MagentrixError) {
		console.error('Edit failed:', error.message)
	}
}

// Batch update — single API call
try {
	await dataService.edit('Account', [
		{ Id: '00I00000000003x0001', Status: 'Active' },
		{ Id: '00I00000000003y0001', Status: 'Inactive' }
	])
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Batch edit failed:', error.message)
	}
}

upsert(entityName: string, data: any | any[])

Creates or updates records. If an Id is provided and the record exists, it is updated. If no Id is provided, a new record is created. Supports batch operations.

import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'

try {
	const records = await dataService.upsert('Contact', [
		{ Id: '00300000000001A0001', Name: 'John Doe', Email: 'john@example.com' },
		{ Name: 'Jane Smith', Email: 'jane@example.com' }
	])
	console.log('Upserted:', records.length, 'records')
} catch (error) {
	if (error instanceof DatabaseError) {
		error.getErrors().forEach(err => {
			console.error(`${err.fieldName}: ${err.message}`)
		})
	} else if (error instanceof MagentrixError) {
		console.error('Upsert failed:', error.message)
	}
}

delete(entityName: string, id: string, permanent?: boolean)

Deletes a record by ID. Performs a soft delete by default — the record is deactivated but can be restored. Pass true as the third argument for permanent deletion.

import { MagentrixError } from '@magentrix-corp/magentrix-sdk'

// Soft delete (default)
try {
	await dataService.delete('Account', '00I00000000003x0001')
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Delete failed:', error.message)
	}
}

// Permanent delete
try {
	await dataService.delete('Account', '00I00000000003x0001', true)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Delete failed:', error.message)
	}
}

deleteMany(entityName: string, ids: string[], permanent?: boolean)

Deletes multiple records by ID in a single API call. Supports soft and permanent deletion.

import { MagentrixError } from '@magentrix-corp/magentrix-sdk'

try {
	await dataService.deleteMany('Contact', [
		'00300000000001A0001',
		'00300000000001B0001',
		'00300000000001C0001'
	])
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Delete many failed:', error.message)
	}
}

getUserInfo()

Returns profile and role information about the currently authenticated user as a UserInfo object.

import { MagentrixError, type UserInfo } from '@magentrix-corp/magentrix-sdk'

try {
	const userInfo: UserInfo = await dataService.getUserInfo()
	console.log('Name:', userInfo.name)
	console.log('Role:', userInfo.roleType)
	console.log('Locale:', userInfo.locale)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Failed to get user info:', error.message)
	}
}

UserInfo Properties:

PropertyTypeDescription
idstringThe user's unique ID.
namestringFull display name.
userNamestringLogin username.
roleTypestringRole type: 'guest', 'employee', 'customer', 'partner', or 'admin'.
langstringLanguage code.
localestringLocale setting.
user_timezonenumberTimezone offset.
preferred_currencystringPreferred currency ISO code.
userCurrencySymbolstringCurrency symbol.
display_user_currencybooleanWhether to display amounts in the user's currency.
guestbooleanWhether the user is a guest.
imprbooleanWhether the user is using delegated access (impersonation).

execute(path: string, model?: any, method?: RequestMethod)

Calls a custom controller action. Defaults to POST if no method is specified. Use this method only when the SDK's standard CRUD methods cannot fulfil the requirement.

import { MagentrixError, RequestMethod } from '@magentrix-corp/magentrix-sdk'

// POST request (default)
try {
	const result = await dataService.execute(
		'/acls/OrderController/ProcessOrder',
		{ accountId: '00I00000000003x0001', amount: 500 }
	)
	console.log('Result:', result)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Request failed:', error.message)
	}
}

// GET request — no body permitted
try {
	const data = await dataService.execute(
		'/acls/ReportController/GetSummary',
		null,
		RequestMethod.get
	)
	console.log('Summary:', data)
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Request failed:', error.message)
	}
}
⚠️ Warning: GET requests must not include a request body. The SDK throws a MagentrixError if a body is passed with RequestMethod.get.

createSession(refreshToken?: string)

Creates a new session manually. In normal usage you do not need to call this — the SDK creates and refreshes sessions automatically. Use it only when you require explicit control over session timing.

import { MagentrixError } from '@magentrix-corp/magentrix-sdk'

try {
	const session = await dataService.createSession()
	console.log('Token:', session.token)
	console.log('Expires in:', session.expires_in, 'seconds')
} catch (error) {
	if (error instanceof MagentrixError) {
		console.error('Session creation failed:', error.message)
	}
}

Error Handling

The SDK provides two error classes. Always check for DatabaseError first since it extends MagentrixError.

MagentrixError — General SDK errors covering authentication failures, network issues, and API-level errors.

DatabaseError — Extends MagentrixError. Thrown on validation failures during create, edit, or upsert. Provides hasErrors() and getErrors() to inspect individual field-level errors.

import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'

try {
	await dataService.create('Account', {
		Name: 'Acme Corporation',
		Email: 'contact@acme.com'
	})
} catch (error) {
	if (error instanceof DatabaseError) {
		// Field-level validation errors
		error.getErrors().forEach(err => {
			console.error(`${err.fieldName}: ${err.message} (Code: ${err.code})`)
		})
	} else if (error instanceof MagentrixError) {
		// General API or auth error
		console.error('API Error:', error.message)
	} else {
		console.error('Unexpected error:', error)
	}
}

Common Error Codes

HTTP StatusError CodeDescription
400PAYLOAD_MISSINGRequest body is empty. A valid JSON payload is required.
400ENTITY_ID_MISMATCHThe ID in the request body does not match the record ID in the URL.
401Session expired. Re-authenticate.
403403Invalid token or too many failed authentication attempts.
404INVALID_ENTITYThe specified entity does not exist.
404MISSING_ENTITYRecord no longer exists.
404ENTITY_NOT_FOUNDRecord was not found or the current user lacks access.
406Missing_FieldA required field was not provided.
413PAYLOAD_TOO_LARGEThe payload exceeds the 20 MB size limit.
500An unexpected server error occurred.

IrisDataResponse — Server Response Contract

When calling query(), retrieve(), or execute() targeting a controller that returns IrisData(), the server returns a structured envelope called IrisDataResponse. Understanding this structure is essential for consuming entity data and building dynamic UIs.

Response Structure

Every IrisDataResponse can contain a mix of entity data — enriched with metadata, permissions, and field-level security — and plain data — serialised as-is with no enrichment.

{
  "records": [ { "Id": "001...", "Name": "Acme Corp", "Status": "Active" } ],
  "totalCount": 42,
  "pageTitle": "Accounts",
  "__entityTypes": {
    "records": "Account"
  },
  "__permissions": {
    "records": {
      "create": true,
      "items": [
        { "id": "001...", "update": true, "delete": false }
      ]
    }
  },
  "__metadatas": {
    "Account": {
      "name": "Account",
      "label": "Account",
      "fields": [ ... ],
      "recordTypes": [ ... ]
    }
  }
}

In this example, records is entity data (listed in __entityTypes), while totalCount and pageTitle are plain values with no metadata or permission enrichment.

Entity Types Map (__entityTypes)

Maps each response property name to its entity type. Properties not listed here are plain data.

const response = await dataService.execute('/acls/AccountController/GetDashboard')

if (response.__entityTypes) {
	for (const [propertyName, entityName] of Object.entries(response.__entityTypes)) {
		console.log(`${propertyName} contains ${entityName} records`)
	}
}

Permissions (__permissions)

Contains per-record CRUD permissions for entity properties. Permissions are only present when the controller explicitly requests them using the With flags enum (Create = 1, Update = 2, Delete = 4).

// Check entity-level create permission
const canCreate = response.__permissions?.records?.create ?? false

// Check per-record update and delete permissions
response.__permissions?.records?.items?.forEach(item => {
	console.log(`Record ${item.id}: update=${item.update}, delete=${item.delete}`)
})

Metadata (__metadatas)

Contains full entity metadata for every entity type in the response. When records include populated lookup fields, the metadata for the referenced entity types is also included automatically — so the UI can render lookup details without additional API calls.

const accountMeta = response.__metadatas?.Account
if (accountMeta) {
	console.log('Label:', accountMeta.label)
	console.log('Fields:', accountMeta.fields.length)
}

Entity Metadata Properties

PropertyTypeDescription
fieldsEntityFieldMetadata[]Full list of field definitions for this entity.
idstringUnique entity ID.
isCustombooleanWhether this is a custom entity.
isPermissionablebooleanWhether role-based permissions apply.
keyPrefixstringID prefix for records of this entity type.
labelstringDisplay label (localised if multi-language is enabled).
namestringAPI name of the entity.
pluralLabelstringPlural display label.
recordTypesRecordTypeMetadata[]Record type definitions with picklist restrictions.
relationshipsEntityRelationshipMetadata[]Relationship definitions for reference fields.

Field Metadata Properties

Each field in the fields array provides comprehensive information for building dynamic forms and list views.

PropertyTypeDescription
fieldTypestringData type: ID, Text, Number, Currency, Percent, Date, DateTime, Email, Phone, Url, Picklist, PicklistMultiSelect, Lookup, MasterDetail, RichText, CheckBox, and others.
idstringUnique field ID.
isCalculatedbooleanWhether the field is a formula or calculated field.
isCustomFieldbooleanWhether this is a custom field.
isFilterablebooleanWhether the field supports filtering.
isReadablebooleanWhether the current user can read this field. If false, the field value is stripped from the response before it reaches the client.
isReadOnlybooleanWhether the field is read-only.
isReferencebooleanWhether this is a lookup or master-detail field.
isRequiredbooleanWhether the field is required on create or edit.
isSortablebooleanWhether the field supports sorting.
labelstringLocalised display label.
lengthnumberMaximum string length.
namestringAPI name of the field.
picklistEntriesPicklistEntry[]Available options for picklist fields.
referenceTostringTarget entity name for lookup and master-detail fields.
referenceTypestringReference type: None, MasterDetail, or Lookup.
💡 Note: Field-level security is enforced at the server serialisation layer. If a user's role does not have read access to a field, that field is omitted from the response regardless of what the controller returns.

TypeScript Exports

The SDK exports the following types and classes for full type safety:

import {
	MagentrixClient,
	MagentrixConfig,
	SessionInfo,
	UserInfo,
	MagentrixError,
	DatabaseError,
	RequestMethod,
	ContentType
} from '@magentrix-corp/magentrix-sdk'

What to Read Next

If you want to…Go to…
Learn the full MEQL query syntaxMEQL Reference
Understand the REST API endpoints the SDK wrapsREST API V3 Reference
Call a custom controller from the SDKC# Controllers
Apply Vue component conventions when using the SDKVue.js Component Development
Last updated on 5/14/2026

Attachments