The Infor OS ION API Gateway is the centralized API management layer for all Infor cloud products. It acts as a NodeJS-based reverse proxy that receives HTTP requests from external clients, enforces security and throttling policies, and forwards them to target Infor applications (M3, LN, CloudSuite Distribution/Industrial/etc.). Every Landmark web service is accessible through the gateway, meaning every field of every screen of every Infor Landmark-based application can be read or written via REST.
This card covers the cloud-hosted ION API Gateway (Infor OS 2024.x/2025.x). On-premise ION Grid REST API v2 has a different endpoint structure and authentication model. The gateway also supports SOAP-to-REST transformation via Handlebars-based endpoint policies.
| Property | Value |
|---|---|
| Vendor | Infor |
| System | Infor OS / ION API Gateway (2024.x / 2025.x) |
| API Surface | REST (primary), SOAP (via transformation) |
| Current API Version | 2024.x / 2025.x (release-based) |
| Editions Covered | All CloudSuite editions (Industrial, Distribution, M3, LN, etc.) |
| Deployment | Cloud (multi-tenant) |
| API Docs | Infor Developer Portal — API Gateway |
| Status | GA |
| API Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| ION API Gateway (REST) | HTTPS/JSON | Individual record CRUD, screen-level operations | Application-dependent | Configurable per endpoint | Yes | No |
| Compass V2 API | HTTPS/CSV or NDJSON | Data Lake queries, analytics, bulk reads | 100,000 rows / 10 MB per page | 10,000 calls/min per tenant | No (async) | Yes |
| ION Connect (BODs) | XML/OAGIS | Event-driven publish-subscribe messaging | N/A (message-based) | ION Messaging limits | Yes (event-driven) | Via batch BODs |
| ION Mapper | File-based (CSV/XML) | Legacy data transformation, file import/export | File-size dependent | N/A | No | Yes |
| Landmark Web Services | REST (proxied) or SOAP | Screen-level field access, transaction processing | Application-dependent | Shared with gateway | Yes | No |
| M3 API REST v2 | HTTPS/JSON | M3 program calls (execute endpoint) | Application-dependent | Shared with gateway | Yes | No |
| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Default gateway timeout | 1 minute | All ION API requests | Extendable to max 5 minutes per endpoint |
| Buffered policy payload limit | 10 MB | Requests using transformation policies | No payload limit without buffered policies |
| Compass V2 max rows per page | 100,000 rows | Compass query results | Also subject to 10 MB size cap |
| Compass V2 max page size | 10 MB | Compass query results | Whichever limit is hit first applies |
| Compass query timeout | 60 minutes | Compass SQL queries | Long-running queries abort after this |
| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| Compass /v2/compass/result calls | 10,000 | Per minute, per tenant | Highest quota among Compass endpoints |
| Compass result availability | ~20 hours | After query FINISHED status | Must retrieve results within this window |
| spikeArrest.maxRequestsPerPeriod | Configurable (e.g., 1,000) | Per timePeriodInMilliseconds | Set per API suite endpoint policy |
| Quota policy | Configurable (allow + interval) | Per endpoint | Can be per-user or global |
| rateSmoothing.delayAfterCount | Configurable (e.g., 1,000) | Per time period | Gradual delay after count exceeded |
Infor does not publish a single global rate limit. Instead, throttling is configured per API suite endpoint via three policy types:
allow requests per interval timeUnit. Can be per-user or per-endpoint.delayAfterCount requests, each subsequent request is delayed.maxRequestsPerPeriod sets absolute maximum.{
"timePeriodInMilliseconds": 60000,
"rateSmoothing": {
"delayAfterCount": 1000,
"delayFactorInMilliseconds": 1000
},
"spikeArrest": {
"maxRequestsPerPeriod": 1000
}
}
| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| Resource Owner Grant | Server-to-server backend integrations | 2 hours (default) | Yes | Uses service account AccessKey + SecretKey |
| Authorization Code Grant | Web apps, mobile apps requiring user login | 2 hours (default) | Yes | Requires redirect URI |
| SAML Bearer Grant | Applications within Infor Ming.le / OS portal | Session-scoped | Reuses SSO token | Already-authenticated context |
| Implicit Grant | Single-page apps (SPAs) | Short-lived | No | Not recommended for server-to-server |
.ionapi credentials file. [src3]START — User needs to integrate with Infor CloudSuite via ION API Gateway
|-- What's the integration pattern?
| |-- Real-time (individual records, <1s)
| | |-- Infor Landmark-based app? (M3, LN, CloudSuite)
| | | |-- YES --> ION API Gateway: Landmark web services via REST proxy
| | | +-- NO --> Register custom API suite (Swagger/OpenAPI)
| | +-- Need screen-level field access?
| | |-- YES --> Landmark web services (every field accessible)
| | +-- NO --> M3 API REST v2 (m3api-rest/v2/execute)
| |-- Batch/Bulk (scheduled, high volume)
| | |-- Data volume < 100,000 records?
| | | |-- YES --> Compass V2 API (single query, paginate)
| | | +-- NO --> Compass V2 with query chunking
| | +-- Need write operations (inbound)?
| | |-- YES --> ION Connect BOD messaging
| | +-- NO --> Compass V2 for read-only bulk export
| |-- Event-driven (real-time notifications)
| | |-- Infor-to-Infor?
| | | |-- YES --> ION Connect publish-subscribe (BOD-based)
| | | +-- NO --> ION Connect + ION API connection point
| | +-- Need guaranteed delivery?
| | |-- YES --> ION Connect with acknowledgment workflow
| | +-- NO --> ION API Gateway webhook
| +-- File-based (CSV/XML)
| +-- ION Mapper + ION Connect document flows
|-- Which direction?
| |-- Inbound --> check per-endpoint write throttling
| |-- Outbound --> check Compass quotas (10K/min result calls)
| +-- Bidirectional --> design conflict resolution with BOD acknowledgment
+-- Error tolerance?
|-- Zero-loss --> ION Connect with dead letter queue + BOD replay
+-- Best-effort --> REST API with retry + exponential backoff
| Operation | Method | Endpoint Pattern | Payload | Notes |
|---|---|---|---|---|
| Obtain OAuth token | POST | {tokenEndpoint} | grant_type=password&username=...&password=... | Token URL from .ionapi file |
| Refresh token | POST | {tokenEndpoint} | grant_type=refresh_token&refresh_token=... | Use before access token expiry |
| Revoke token | POST | {revokeEndpoint} | token=...&token_type_hint=access_token | Call on integration shutdown |
| Call Landmark service | GET/POST | /{suite}/{service}/{endpoint} | JSON | Bearer token required |
| M3 API execute | POST | /m3api-rest/v2/execute/{program}/{transaction} | JSON | M3-specific programs |
| Submit Compass query | POST | /v2/compass/jobs | SQL (text/plain) | Async — poll for status |
| Get Compass results | GET | /v2/compass/jobs/{queryId}/result | N/A | Paginated; max 100K rows / 10 MB |
| IFS User info | GET | /ifsservice/usermgt/v2/users/me | N/A | Quick auth validation test |
The .ionapi file contains all OAuth endpoints and credentials for your tenant. Download from Infor OS Portal > ION API > Authorized Apps. [src3]
{
"ti": "TENANT_ID",
"cn": "CLIENT_NAME",
"ci": "CLIENT_ID",
"cs": "CLIENT_SECRET",
"iu": "https://inforos-{region}.inforcloudsuite.com",
"pu": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/token.oauth2",
"oa": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/authorization.oauth2",
"or": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/revoke_token.oauth2"
}
Verify: Confirm the file contains non-empty ci, cs, pu, and service account fields.
Use the Resource Owner grant for backend integrations. [src3, src4]
curl -X POST "{pu}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "username={saession}" \
-d "password={sapassword}" \
-d "client_id={ci}" \
-d "client_secret={cs}"
Verify: Response includes access_token and refresh_token with token_type: Bearer.
With the bearer token, call any registered API suite endpoint. [src3]
curl -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
-H "Authorization: Bearer {access_token}" \
-H "Accept: application/json"
Verify: HTTP 200 response with JSON payload containing user details.
Submit a query, poll for completion, then paginate results. [src6]
# Submit query
curl -X POST ".../v2/compass/jobs" -H "Content-Type: text/plain" -d "SELECT * FROM table LIMIT 50000"
# Poll status (repeat until FINISHED)
curl -X GET ".../v2/compass/jobs/{queryId}" -H "Authorization: Bearer {token}"
# Retrieve results with pagination
curl -X GET ".../v2/compass/jobs/{queryId}/result?resultFormat=application/x-ndjson&offset=0&limit=10000"
Verify: Result set returns with expected row count; increment offset by limit for next page.
Access tokens expire after 2 hours. Refresh before expiry. [src4]
curl -X POST "{pu}" \
-d "grant_type=refresh_token&refresh_token={token}&client_id={ci}&client_secret={cs}"
Verify: New access_token and refresh_token returned.
# Input: .ionapi credentials file path
# Output: JSON response from ION API endpoint
import json
import requests # requests==2.31.0
def load_ionapi_credentials(filepath):
with open(filepath, 'r') as f:
return json.load(f)
def get_access_token(creds):
resp = requests.post(creds['pu'], data={
'grant_type': 'password',
'username': creds.get('saession', creds.get('saak', '')),
'password': creds.get('sapassword', creds.get('sask', '')),
'client_id': creds['ci'],
'client_secret': creds['cs'],
}, timeout=30)
resp.raise_for_status()
return resp.json()
def call_ion_api(base_url, tenant, endpoint, token):
url = f"{base_url}/{tenant}/{endpoint}"
resp = requests.get(url, headers={
'Authorization': f"Bearer {token}",
'Accept': 'application/json',
}, timeout=60)
if resp.status_code == 429:
retry_after = int(resp.headers.get('Retry-After', 60))
raise Exception(f"Rate limited. Retry after {retry_after}s")
resp.raise_for_status()
return resp.json()
creds = load_ionapi_credentials('my_app.ionapi')
tokens = get_access_token(creds)
result = call_ion_api(creds['iu'], creds['ti'],
'ifsservice/usermgt/v2/users/me', tokens['access_token'])
// Input: .ionapi credentials, SQL query string
// Output: All query result rows (paginated)
const fs = require('fs');
const axios = require('axios'); // [email protected]
async function compassQuery(ionapiPath, sql) {
const creds = JSON.parse(fs.readFileSync(ionapiPath, 'utf8'));
const tokenResp = await axios.post(creds.pu, new URLSearchParams({
grant_type: 'password',
username: creds.saession || creds.saak,
password: creds.sapassword || creds.sask,
client_id: creds.ci,
client_secret: creds.cs,
}).toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
const token = tokenResp.data.access_token;
const baseUrl = `${creds.iu}/${creds.ti}`;
const headers = { Authorization: `Bearer ${token}` };
// Submit query
const jobResp = await axios.post(`${baseUrl}/v2/compass/jobs`, sql,
{ headers: { ...headers, 'Content-Type': 'text/plain' } });
const queryId = jobResp.data.queryId;
// Poll with backoff
let status = 'RUNNING', delay = 1000;
while (status !== 'FINISHED' && status !== 'FAILED') {
await new Promise(r => setTimeout(r, delay));
const s = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}`, { headers });
status = s.data.status;
delay = Math.min(delay * 2, 30000);
}
// Paginate results
const allRows = [];
let offset = 0, limit = 10000, hasMore = true;
while (hasMore) {
const r = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}/result`,
{ params: { resultFormat: 'application/x-ndjson', offset, limit }, headers });
const rows = r.data.split('\n').filter(Boolean).map(JSON.parse);
allRows.push(...rows);
hasMore = rows.length === limit;
offset += limit;
}
return allRows;
}
# Input: .ionapi credentials (manually extracted)
# Output: User info JSON confirming successful auth
TOKEN=$(curl -s -X POST "{tokenEndpoint}" \
-d "grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}" \
| jq -r '.access_token')
curl -s "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
-H "Authorization: Bearer $TOKEN" | jq .
| Source/Concept | ION API Equivalent | Type | Transform | Gotcha |
|---|---|---|---|---|
| REST request body | JSON payload to Landmark service | JSON | Direct pass-through | Gateway may transform if endpoint policies configured |
| SOAP request | JSON via Handlebars transformation | XML-to-JSON | Policy-defined template | 10 MB payload limit with buffered policies |
| Compass SQL query | text/plain POST body | String | Direct SQL | Must match Data Lake schema, not app schema |
| Compass results | CSV or NDJSON | Streaming | Format via resultFormat param | CSV lacks type info; NDJSON preserves types |
| BOD documents | OAGIS-standard XML | XML | ION Mapper transformations | BOD schema is fixed by OAGIS standard |
| Auth credentials | .ionapi JSON file | JSON config | Extract pu, ci, cs, saession | Field names vary between file versions |
saession/sapassword; newer versions may use saak/sask. Code must handle both. [src4]| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 401 | Unauthorized | Token expired or invalid | Refresh token and retry; check .ionapi credentials |
| 403 | Forbidden | Insufficient permissions | Verify ION API authorized app has correct security roles in IFS |
| 404 | Not Found | Endpoint not registered | Check API suite registration; verify proxy context path |
| 408 | Request Timeout | Gateway timeout exceeded (default 1 min) | Increase timeout (max 5 min) or switch to async pattern |
| 429 | Too Many Requests | Throttling/spikeArrest limit hit | Exponential backoff; respect Retry-After header |
| 500 | Internal Server Error | Backend application error | Check ION API logs; verify backend health |
| 502 | Bad Gateway | Target unreachable | Check application server status; retry with backoff |
| 503 | Service Unavailable | Gateway or backend overloaded | Wait and retry; check Infor Status page |
Monitor for 401 errors and trigger credential file reload. [src3]Retrieve and persist results immediately after query completion. [src6]Remove buffered policies for large-payload endpoints or paginate. [src7]Configure endpoint-level timeout extension (max 5 min). [src7]Monitor response times, not just error rates; implement circuit breaker. [src5]# BAD — hardcoded token URL breaks when migrating between tenants or regions
TOKEN_URL = "https://inforos-us.inforcloudsuite.com/MYTENANT/as/token.oauth2"
# GOOD — token URL is tenant-specific and changes with migration
creds = json.load(open('my_app.ionapi'))
TOKEN_URL = creds['pu'] # always correct for this tenant
// BAD — burns through API quota (10K calls/min) and adds unnecessary load
while (status !== 'FINISHED') {
const resp = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}`);
status = resp.data.status;
}
// GOOD — respectful polling with increasing intervals
let delay = 1000;
while (status !== 'FINISHED' && status !== 'FAILED') {
await new Promise(r => setTimeout(r, delay));
const resp = await axios.get(url, { headers });
status = resp.data.status;
delay = Math.min(delay * 2, 30000); // max 30s between polls
}
# BAD — large data export times out at 60 seconds
response = requests.get(f"{base_url}/myapp/api/export-all",
headers=headers, timeout=300) # client timeout is 5 min but gateway kills at 1 min
# GOOD — Compass handles long-running queries asynchronously
job = requests.post(f"{base_url}/v2/compass/jobs",
data="SELECT * FROM large_table",
headers={**headers, 'Content-Type': 'text/plain'})
query_id = job.json()['queryId']
# Poll for completion, then paginate results
Test each endpoint's throttling behavior independently or review API suite configuration. [src5]Resource Owner for backends, Authorization Code for web/mobile, SAML Bearer for Ming.le-embedded apps. [src3]Check for both field name variants (saession/sapassword vs saak/sask) with fallbacks. [src4]Use ION API Gateway REST endpoints for real-time; reserve Compass for analytics and bulk reads. [src6]Avoid transformation policies for high-volume endpoints, or paginate at application layer. [src7]# Test authentication — obtain access token
curl -s -X POST "{tokenEndpoint}" \
-d "grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}" \
| jq '{access_token: .access_token, token_type: .token_type, expires_in: .expires_in}'
# Verify token validity — get current user info
curl -s -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
-H "Authorization: Bearer {token}" | jq .
# Test a specific API suite endpoint
curl -s -o /dev/null -w "HTTP %{http_code} — %{time_total}s\n" \
-X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/{suite}/{endpoint}" \
-H "Authorization: Bearer {token}"
# Check Compass query status
curl -s "https://inforos-{region}.inforcloudsuite.com/{tenant}/v2/compass/jobs/{queryId}" \
-H "Authorization: Bearer {token}" | jq '{status: .status, rowCount: .rowCount}'
# Revoke token (cleanup)
curl -s -X POST "{revokeEndpoint}" \
-d "token={token}&token_type_hint=access_token&client_id={clientId}&client_secret={clientSecret}"
| Platform Release | Release Period | Status | Key Changes | Migration Notes |
|---|---|---|---|---|
| Infor OS 2025.x | 2025 H1 | Current | Compass V2 GA, enhanced throttling | Compass V1 deprecated — migrate to V2 |
| Infor OS 2024.x | 2024 | Supported | BaaS endpoint policies, OpenAPI 3.0.x | New policy configuration format |
| Infor OS 2022.x | 2022 | Supported (limited) | SOAP-to-REST via Handlebars, Swagger 2.0 | Older .ionapi credential format |
| Infor OS 12.0.x | 2020-2021 | EOL | Initial Compass V1 APIs | Must upgrade to 2024.x+ for Compass V2 |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Real-time REST API calls to Infor Landmark apps | High-volume bulk export > 100K records in real-time | Compass V2 API (async, paginated) |
| Server-to-server integration with OAuth 2.0 | Event-driven publish-subscribe messaging | ION Connect with BOD workflows |
| Unified auth across multiple Infor applications | Direct database access to Infor backend | Never bypass gateway for cloud deployments |
| SOAP-to-REST transformation for legacy services | On-premise ION Grid REST API integration | ION Grid REST API v2 (different base URL/auth) |
| Web/mobile apps needing user-context API access | File-based batch import/export (CSV/XML) | ION Mapper + ION Connect document flows |