npx skills add imbilawork/2nth-skills@sagex3-ai
OVERVIEW
Sage X3 (formerly Sage Enterprise Management) is a mid-market ERP powering manufacturing, distribution, and services companies. Modern instances expose a GraphQL API via the Syracuse web server using the Yoga GraphQL engine.
This skill gives AI agents the knowledge to query, report on, and integrate with Sage X3 — without hallucinating endpoints or field names.
ARCHITECTURE
The recommended integration pattern uses a Cloudflare Worker as a proxy — this avoids CORS, secures credentials at the edge, and enables AI analysis via Workers AI.
Proxy + AI
GraphQL API
Endpoint Pattern
https://<host>:<port>/<folder>/api
Authentication
Authorization: Basic base64(USERNAME:PASSWORD)
Never expose Sage X3 credentials in client-side code. Use a server-side proxy with secrets stored via wrangler secret put or environment variables.
CAPABILITIES
What your agent can do with this skill installed:
- Query master data — customers, suppliers, products, stock sites
- Pull transactions — sales orders, purchase orders, invoices, deliveries
- Check inventory — stock levels, reorder alerts, warehouse positions
- Generate reports — revenue by customer, aged debtors, sales trends
- Introspect the schema — discover what's available on any Sage X3 instance
- Build dashboards — structure data for Chart.js, tables, and KPI cards
- Natural language queries — translate questions into GraphQL
- Deploy to edge — Cloudflare Workers proxy with AI analysis
SCHEMA MAP
Sage X3 organizes its GraphQL API by business domain. Each domain contains entities that follow the Relay pagination pattern.
Schema varies by installation — modules, custom fields, and endpoints differ. Always introspect first with __schema queries.
QUERY PATTERNS
Relay Pagination
All list queries use edges / node with cursor-based pagination:
query {
x3MasterData {
customer {
query(first: 10, after: "cursor") {
edges {
node {
code {
code
companyName1
country { code countryName }
currency { code }
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
Customer Master Data
query Customers {
x3MasterData {
customer {
query(first: 50) {
edges {
node {
code {
code
companyName1
country { code countryName }
currency { code localizedDescription }
isCustomer
isSupplier
creditLimit
paymentTerms { code localizedDescription }
}
}
}
}
}
}
}
Sales Orders
query SalesOrders {
x3Sales {
salesOrder {
query(first: 25) {
edges {
node {
code {
orderNumber
orderDate
customer { code companyName1 }
totalAmount
status
lines {
product { code localizedDescription1 }
quantity
unitPrice
lineAmount
}
}
}
}
}
}
}
}
OData-style Filtering
# Active South African customers
query(first: 50, filter: "country.code eq 'ZA' and isCustomer eq true")
# Orders this year
query(first: 100, filter: "orderDate ge '2026-01-01'")
| Operator | Meaning | Example |
|---|---|---|
eq | Equals | status eq 'Active' |
ne | Not equals | country.code ne 'ZA' |
gt / ge | Greater than (or equal) | totalAmount gt 10000 |
lt / le | Less than (or equal) | orderDate lt '2026-03-01' |
contains() | Substring match | contains(companyName1, 'Umbrella') |
and / or | Logical | isCustomer eq true and isSupplier eq true |
REPORTING PATTERNS
Sage X3 GraphQL doesn't support aggregation — fetch data and aggregate client-side.
Revenue by Customer
query RevenueReport {
x3Sales {
salesOrder {
query(first: 1000, filter: "orderDate ge '2026-01-01'") {
edges {
node {
code {
customer { code companyName1 }
totalAmount
currency { code }
orderDate
}
}
}
}
}
}
}
// Then group by customer.code, sum totalAmount
Aged Debtors
query AgedDebtors {
x3Sales {
salesInvoice {
query(first: 500, filter: "outstandingAmount gt 0") {
edges {
node {
code {
invoiceNumber
invoiceDate
dueDate
customer { code companyName1 }
totalAmount
outstandingAmount
}
}
}
}
}
}
}
// Age buckets: Current, 30d, 60d, 90d+
Inventory Alerts
Fetch stock levels and filter where quantityOnHand < reorderPoint.
AI INTEGRATION
The power pattern: natural language in, formatted report out.
GraphQL
Sage X3
results
Cloudflare Worker Proxy
async function handleGraphQL(request, env) {
const { query, variables } = await request.json();
const credentials = btoa(
`${env.SAGE_X3_USERNAME}:${env.SAGE_X3_PASSWORD}`
);
const response = await fetch(env.SAGE_X3_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${credentials}`,
},
body: JSON.stringify({ query, variables }),
});
return new Response(response.body, {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
}
Natural Language System Prompt
You are a Sage X3 GraphQL expert. Given a natural
language question, generate the appropriate GraphQL query.
The API uses Relay pagination (edges/node) with domains:
x3MasterData, x3Sales, x3Purchasing, x3Stock, x3Accounting.
Always include the `first` parameter for pagination.
Respond with ONLY the GraphQL query.
INSTALL
Works with Claude Code, Cursor, Windsurf, Cline, and 30+ other AI agents:
npx skills add imbilawork/2nth-skills@sagex3-ai
Or install globally with no prompts:
npx skills add imbilawork/2nth-skills@sagex3-ai -g -y
The skill is loaded automatically when your agent works on Sage X3 related tasks.
GOTCHAS
| Issue | Detail |
|---|---|
Fields under code | All data lives inside node.code, not directly on node |
| Pagination required | Always pass first; omitting it may return 0 results |
| CORS blocked | Browser requests fail — use a server-side proxy |
| Schema varies | Always introspect; installed modules and custom fields differ |
| No aggregation | GraphQL doesn't support GROUP BY — aggregate client-side |
| Date format | ISO 8601 in filters: '2026-01-01' |
| Self-signed certs | Dev instances may need NODE_TLS_REJECT_UNAUTHORIZED=0 |
| Rate limits | Syracuse has connection limits — batch queries where possible |