---
# === IDENTITY ===
id: business/erp-integration/infor-ion-api-gateway/2026
canonical_question: "What are the Infor OS / ION API Gateway REST capabilities, rate limits, and pagination patterns?"
aliases:
  - "Infor ION API Gateway REST API capabilities and limits"
  - "How to integrate with Infor CloudSuite via ION API Gateway"
  - "Infor OS API Gateway throttling and pagination"
  - "ION API OAuth2 authentication and rate limiting"
entity_type: erp_integration
domain: business > erp-integration > infor-ion-api-gateway
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Infor OS / ION API Gateway"
    vendor: "Infor"
    version: "2024.x / 2025.x"
    edition: "CloudSuite (all editions)"
    deployment: "cloud"
    api_surface: "REST"

# === VERIFICATION ===
last_verified: 2026-03-02
confidence: 0.82
version: 1.0
first_published: 2026-03-02

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "2024.x — Compass V2 API replaced V1 pagination"
  next_review: 2026-08-29
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Default gateway timeout is 1 minute; extendable to max 5 minutes — long-running queries must use async patterns"
  - "Buffered policies have a 10 MB payload limit — large file transfers must bypass the gateway"
  - "Compass V2 pagination caps at 100,000 rows or 10 MB per page — split larger result sets"
  - "Compass query results expire ~20 hours after FINISHED status — retrieve promptly or re-query"
  - "Compass /v2/compass/result quota: up to 10,000 calls per minute per tenant"
  - "OAuth 2.0 access tokens expire after 2 hours by default — implement refresh token logic"
  - "Throttling spikeArrest maxRequestsPerPeriod is configurable per endpoint — not a fixed global limit"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs Infor ION document flow or BOD messaging (XML-based)"
    use_instead: "Infor ION Connect / ION Desk documentation"
  - condition: "User needs on-premise ION Grid REST API (not cloud gateway)"
    use_instead: "Infor ION Grid REST API v2 documentation"
  - condition: "User needs Infor Landmark web services directly (bypassing gateway)"
    use_instead: "Infor Landmark Technology Web Services Reference Guide"

# === AGENT HINTS ===
inputs_needed:
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "real-time sync (individual records, <1s latency)"
      - "batch/bulk (scheduled, high volume)"
      - "event-driven (BOD publish-subscribe, ION Connect)"
      - "file-based (CSV/XML import/export via ION Mapper)"
  - key: data_volume
    question: "What's your daily data volume?"
    type: choice
    options:
      - "< 1,000 records/day"
      - "1,000-100,000 records/day"
      - "> 100,000 records/day"
  - key: direction
    question: "What's the data flow direction?"
    type: choice
    options:
      - "inbound (writing to Infor CloudSuite)"
      - "outbound (reading from Infor CloudSuite)"
      - "bidirectional sync"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/infor-ion-api-gateway/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-02)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to: []
  solves: []
  alternative_to: []
  often_confused_with: []

# === SOURCES ===
sources:
  - id: src1
    title: "API Gateway — Infor OS Administration Guide (2024.x)"
    author: Infor
    url: https://docs.infor.com/inforos/2024.x/en-us/useradminlib_cloud/apigatewayag_cloud/vwi1489585692124.html
    type: official_docs
    published: 2024-01-15
    reliability: authoritative
  - id: src2
    title: "API Gateway — Infor Developer Portal Tutorials"
    author: Infor
    url: https://developer.infor.com/tutorials/api-gateway
    type: official_docs
    published: 2024-06-01
    reliability: authoritative
  - id: src3
    title: "How to Call an ION API — Infor Developer Portal"
    author: Infor
    url: https://developer.infor.com/tutorials/api-gateway/how-to-call-an-ion-api
    type: official_docs
    published: 2024-06-01
    reliability: authoritative
  - id: src4
    title: "Infor ION API Gateway SDK — GitHub"
    author: Infor
    url: https://github.com/infor-cloud/ion-api-sdk
    type: community_resource
    published: 2024-09-01
    reliability: high
  - id: src5
    title: "API Gateway Endpoint Policies — BaaS Developer Guide (2024.x)"
    author: Infor
    url: https://docs.infor.com/inforos/2024.x/en-us/useradminlib_cloud/apigatewaybaasdg/lcs1730257685556.html
    type: official_docs
    published: 2024-10-01
    reliability: authoritative
  - id: src6
    title: "Compass V2 APIs Pagination — Infor OS (Latest)"
    author: Infor
    url: https://docs.infor.com/inforos/latest/en-us/useradminlib_cloud/ioncedg_cloud_osm/bca1622476381846.html
    type: official_docs
    published: 2024-08-01
    reliability: authoritative
  - id: src7
    title: "Timeout Limits — API Gateway (2024.x)"
    author: Infor
    url: https://docs.infor.com/inforos/2024.x/en-us/useradminlib_cloud/apigatewayag_cloud/ayc1656493867736.html
    type: official_docs
    published: 2024-01-15
    reliability: authoritative
  - id: src8
    title: "Infor ION Integration Guide: Streamlining Enterprise Systems for 2025"
    author: SAMA Consulting
    url: https://samaconsultinginc.com/blogs/infor-ion-integration-guide-streamlining-enterprise-systems-for-2025/
    type: technical_blog
    published: 2025-01-10
    reliability: moderate_high
---

# Infor OS / ION API Gateway REST Capabilities, Rate Limits & Pagination

## TL;DR

- **Bottom line**: The ION API Gateway is a NodeJS reverse proxy that fronts all Infor CloudSuite applications with OAuth 2.0, throttling policies, and protocol transformation (SOAP-to-REST) — every Landmark screen field is accessible via REST through the gateway. [src2]
- **Key limit**: Default 1-minute timeout (max 5 min); buffered policies capped at 10 MB payload; Compass V2 pagination at 100K rows or 10 MB per page. [src6, src7]
- **Watch out for**: Throttling is per-endpoint configurable (spikeArrest + rateSmoothing), not a single global number — you must check each API suite's policy configuration, and defaults vary by tenant. [src5]
- **Best for**: Real-time REST integration with Infor M3, LN, CloudSuite Distribution, and all Landmark-based applications via a unified OAuth 2.0 gateway. [src2]
- **Authentication**: OAuth 2.0 with four grant types — Resource Owner (server-to-server recommended), Authorization Code (web/mobile), SAML Bearer (Ming.le SSO), Implicit (SPAs). Access tokens valid 2 hours, refreshable. [src3, src4]

## System Profile

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. [src2]

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 — see the ION Grid documentation separately. The gateway also supports SOAP-to-REST transformation via Handlebars-based endpoint policies, allowing legacy WSDL services to be consumed as REST APIs. [src2, src5]

| 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, not numbered) |
| **Editions Covered** | All CloudSuite editions (Industrial, Distribution, M3, LN, etc.) |
| **Deployment** | Cloud (multi-tenant) |
| **API Docs URL** | [Infor Developer Portal — API Gateway](https://developer.infor.com/tutorials/api-gateway) |
| **Status** | GA |

## API Surfaces & Capabilities

| 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 (spikeArrest + quota policies) | 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 (result endpoint) | No (async query) | Yes |
| ION Connect (BODs) | XML/OAGIS | Event-driven publish-subscribe messaging | N/A (message-based) | Subject to 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 (via gateway) | REST (proxied) or SOAP | Screen-level field access, transaction processing | Application-dependent | Shared with gateway throttling | Yes | No |
| M3 API REST v2 | HTTPS/JSON | M3 program calls (execute endpoint) | Application-dependent | Shared with gateway throttling | Yes | No |

[src2, src6]

## Rate Limits & Quotas

### Per-Request Limits

| 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 |

[src6, src7]

### Rolling / Daily Limits

| 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 (example: 1,000) | Per timePeriodInMilliseconds (example: 60,000 ms) | Set per API suite endpoint policy |
| Quota policy | Configurable (allow + interval + timeUnit) | Per endpoint | Can be per-user (userLevel: true) or global |
| rateSmoothing.delayAfterCount | Configurable (example: 1,000) | Per time period | Gradual delay introduced after count exceeded |

[src5, src6]

### Throttling Policy Configuration

Infor does not publish a single global rate limit. Instead, throttling is configured per API suite endpoint via three policy types: [src5]

- **Quota**: Hard cap — `allow` requests per `interval` `timeUnit`. Can be scoped per user (`userLevel: true`) or per endpoint.
- **Throttling (rateSmoothing)**: After `delayAfterCount` requests within `timePeriodInMilliseconds`, each subsequent request is delayed by `delayFactorInMilliseconds`. Provides graceful degradation.
- **Throttling (spikeArrest)**: Hard cutoff — `maxRequestsPerPeriod` sets the absolute maximum within the time period. Requests exceeding this are rejected.

Example configuration:
```json
{
  "timePeriodInMilliseconds": 60000,
  "rateSmoothing": {
    "delayAfterCount": 1000,
    "delayFactorInMilliseconds": 1000
  },
  "spikeArrest": {
    "maxRequestsPerPeriod": 1000
  }
}
```

## Authentication

| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| Resource Owner Grant | Server-to-server backend integrations (recommended) | 2 hours (default) | Yes (via refresh token) | Uses service account AccessKey + SecretKey as username/password |
| Authorization Code Grant | Web apps, mobile apps requiring user login | 2 hours (default) | Yes | Requires redirect URI; user must approve access |
| SAML Bearer Grant | Applications within Infor Ming.le / Infor OS portal | Session-scoped | Reuses SSO token | Already-authenticated context; token reuse |
| Implicit Grant | Single-page apps (SPAs), user-agent apps | Short-lived | No | Not recommended for production server-to-server use |

[src3, src4]

### Authentication Gotchas

- **Service accounts require four credentials, not two**: ClientID + ClientSecret (application-level) AND AccessKey + SecretKey (service account-level). Missing any one causes silent auth failure. [src4]
- **Token endpoint is tenant-specific**: The OAuth token URL is unique per Infor cloud tenant — hardcoding a wrong endpoint is a common migration error. Always extract from the `.ionapi` credentials file. [src3]
- **SAML Bearer only works inside Ming.le**: If your application runs outside the Infor OS portal, SAML Bearer grant will fail. Use Authorization Code or Resource Owner instead. [src3]
- **Access token expiry is admin-configurable**: While the default is 2 hours, tenant admins can change session timeout. Never hardcode the expiry — always handle 401 by refreshing. [src4]

## Constraints

- **No single published global rate limit**: Unlike Salesforce (100K/24h) or Oracle (configurable per tenant), Infor uses configurable per-endpoint throttling policies. You must discover the limits for each API suite through testing or admin configuration review. [src5]
- **Gateway timeout is 1 minute by default**: Any REST call taking longer than 60 seconds will timeout. For long-running operations, use async patterns (Compass queries, ION workflows) or request a timeout extension (max 5 min). [src7]
- **10 MB buffered policy limit**: If your endpoint uses request/response transformation policies (e.g., SOAP-to-REST via Handlebars), the payload is capped at 10 MB. This breaks large data exports using transformation policies. [src7]
- **Compass results are ephemeral**: Query results expire ~20 hours after completion. There is no permanent result storage — if you need the data later, re-run the query or persist to your own storage. [src6]
- **API documentation format required for registration**: Custom API suites must be registered with Swagger 2.0 or OpenAPI 3.0.x specs. Undocumented APIs cannot be exposed through the gateway. [src2]
- **On-premise vs cloud differences**: On-premise ION Grid REST API v2 has a different base URL pattern (`http(s)://server:port/grid/rest`) and may use basic auth or client certificates instead of OAuth 2.0. This card covers cloud-only. [src1]

## Integration Pattern Decision Tree

```
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: use Landmark web services via REST proxy
|   |   |   +-- NO --> ION API Gateway: register custom API suite (Swagger/OpenAPI)
|   |   +-- Need screen-level field access?
|   |       |-- YES --> Landmark web services (every field accessible via gateway)
|   |       +-- NO --> M3 API REST v2 (m3api-rest/v2/execute) for program calls
|   |-- Batch/Bulk (scheduled, high volume)
|   |   |-- Data volume < 100,000 records?
|   |   |   |-- YES --> Compass V2 API (single query, paginate results)
|   |   |   +-- NO --> Compass V2 API with query chunking + result pagination
|   |   +-- Need write operations (inbound)?
|   |       |-- YES --> ION Connect BOD messaging (bulk BOD publishing)
|   |       +-- 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 (BOD --> REST)
|   |   +-- Need guaranteed delivery?
|   |       |-- YES --> ION Connect with acknowledgment workflow
|   |       +-- NO --> ION API Gateway webhook (poll or push)
|   +-- File-based (CSV/XML)
|       +-- ION Mapper for format transformation + ION Connect for routing
|-- Which direction?
|   |-- Inbound (writing to Infor) --> check per-endpoint write throttling
|   |-- Outbound (reading from Infor) --> check Compass quotas (10K/min result calls)
|   +-- Bidirectional --> design conflict resolution with BOD acknowledgment
+-- Error tolerance?
    |-- Zero-loss required --> ION Connect with dead letter queue + BOD replay
    +-- Best-effort acceptable --> REST API with retry + exponential backoff
```

## Quick Reference

| Operation | Method | Endpoint Pattern | Payload | Notes |
|---|---|---|---|---|
| Obtain OAuth token (Resource Owner) | POST | `{tokenEndpoint}` | `grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}` | Token URL from `.ionapi` file |
| Refresh token | POST | `{tokenEndpoint}` | `grant_type=refresh_token&refresh_token={token}&client_id={clientId}&client_secret={clientSecret}` | Use before access token expiry |
| Revoke token | POST | `{revokeEndpoint}` | `token={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 and transactions |
| Submit Compass query | POST | `/v2/compass/jobs` | SQL query (text/plain) | Async — poll for status |
| Get Compass results | GET | `/v2/compass/jobs/{queryId}/result?resultFormat=text/csv&offset={n}&limit={n}` | N/A | Paginated; max 100K rows / 10 MB per page |
| IFS User info | GET | `/ifsservice/usermgt/v2/users/me` | N/A | Quick auth validation test |

[src2, src3, src6]

## Step-by-Step Integration Guide

### 1. Download the .ionapi credentials file

The `.ionapi` file contains all OAuth endpoints and credentials for your tenant. Download it from Infor OS Portal > ION API > Authorized Apps > your application > Download Credentials. [src3]

```json
{
  "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",
  "saession": "AccessKey",
  "sapassword": "SecretKey"
}
```

**Verify**: Confirm the file contains non-empty `ci`, `cs`, `pu`, and `saession`/`sapassword` fields.

### 2. Authenticate and obtain an access token

Use the Resource Owner grant for backend integrations. Exchange the service account credentials for an OAuth 2.0 bearer token. [src3, src4]

```bash
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`.

### 3. Call an ION API endpoint

With the bearer token, call any registered API suite endpoint. The user info endpoint is a good first test. [src3]

```bash
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.

### 4. Handle pagination for Compass V2 queries

Submit a query, poll for completion, then paginate results. [src6]

```bash
# Submit query
curl -X POST "https://inforos-{region}.inforcloudsuite.com/{tenant}/v2/compass/jobs" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: text/plain" \
  -d "SELECT * FROM your_datalake_table LIMIT 50000"

# Poll status (repeat until status=FINISHED)
curl -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/v2/compass/jobs/{queryId}" \
  -H "Authorization: Bearer {access_token}"

# Retrieve results with pagination
curl -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/v2/compass/jobs/{queryId}/result?resultFormat=application/x-ndjson&offset=0&limit=10000" \
  -H "Authorization: Bearer {access_token}" \
  -H "Accept-Encoding: gzip"
```

**Verify**: Result set returns with expected row count; increment `offset` by `limit` for next page.

### 5. Implement token refresh

Access tokens expire after 2 hours. Refresh before expiry to avoid 401 errors in long-running integrations. [src4]

```bash
curl -X POST "{pu}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token={refresh_token}" \
  -d "client_id={ci}" \
  -d "client_secret={cs}"
```

**Verify**: New `access_token` and `refresh_token` returned; update stored tokens.

## Code Examples

### Python: Authenticate and call ION API

```python
# 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):
    """Load credentials from .ionapi file."""
    with open(filepath, 'r') as f:
        return json.load(f)

def get_access_token(creds):
    """Obtain OAuth 2.0 access token via Resource Owner grant."""
    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'],
        },
        headers={'Content-Type': 'application/x-www-form-urlencoded'},
        timeout=30
    )
    resp.raise_for_status()
    return resp.json()

def call_ion_api(base_url, tenant, endpoint, token):
    """Call an ION API endpoint with bearer token."""
    url = f"{base_url}/{tenant}/{endpoint}"
    resp = requests.get(
        url,
        headers={
            'Authorization': f"Bearer {token}",
            'Accept': 'application/json',
        },
        timeout=60  # match gateway default timeout
    )
    if resp.status_code == 429:
        # Throttled — respect Retry-After header
        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()

# Usage
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'])
print(json.dumps(result, indent=2))
```

### JavaScript/Node.js: Compass V2 query with pagination

```javascript
// Input:  .ionapi credentials, SQL query string
// Output: All query result rows (paginated)

const fs = require('fs');
const axios = require('axios'); // axios@1.6.0

async function compassQuery(ionapiPath, sql) {
  const creds = JSON.parse(fs.readFileSync(ionapiPath, 'utf8'));

  // Step 1: Get OAuth token
  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}` };

  // Step 2: Submit query
  const jobResp = await axios.post(
    `${baseUrl}/v2/compass/jobs`, sql,
    { headers: { ...headers, 'Content-Type': 'text/plain' } }
  );
  const queryId = jobResp.data.queryId;

  // Step 3: Poll until FINISHED (max 60 min timeout)
  let status = 'RUNNING';
  while (status !== 'FINISHED' && status !== 'FAILED') {
    await new Promise(r => setTimeout(r, 2000));
    const statusResp = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}`, { headers });
    status = statusResp.data.status;
  }
  if (status === 'FAILED') throw new Error('Compass query failed');

  // Step 4: Paginate results (100K rows / 10MB per page)
  const allRows = [];
  let offset = 0;
  const limit = 10000;
  let hasMore = true;

  while (hasMore) {
    const resultResp = await axios.get(
      `${baseUrl}/v2/compass/jobs/${queryId}/result`,
      {
        params: { resultFormat: 'application/x-ndjson', offset, limit },
        headers: { ...headers, 'Accept-Encoding': 'gzip' },
      }
    );
    const rows = resultResp.data.split('\n').filter(Boolean).map(JSON.parse);
    allRows.push(...rows);
    hasMore = rows.length === limit;
    offset += limit;
  }

  return allRows;
}
```

### cURL: Quick authentication test

```bash
# Input:  .ionapi credentials (manually extracted)
# Output: User info JSON confirming successful auth

# Step 1: Get token
TOKEN=$(curl -s -X POST "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/token.oauth2" \
  -d "grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}" \
  | jq -r '.access_token')

# Step 2: Test API call
curl -s -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq .
```

## Data Mapping

### ION API Gateway Data Patterns

| Source/Concept | ION API Gateway 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 policy | XML-to-JSON | Policy-defined Handlebars template | 10 MB payload limit when using buffered policies |
| Compass SQL query | text/plain POST body | String | Direct SQL | Must match Data Lake schema — not Infor application schema |
| Compass results | CSV or NDJSON (newline-delimited JSON) | Streaming | Format selected via `resultFormat` param | CSV lacks type information; NDJSON preserves types |
| BOD documents | OAGIS-standard XML | XML | ION Mapper transformations | BOD schema is fixed by OAGIS standard — cannot add custom fields |
| Authentication credentials | `.ionapi` JSON file | JSON config | Extract `pu`, `ci`, `cs`, `saession`, `sapassword` | Field names vary between file versions (`saession` vs `saak`) |

[src2, src4, src6]

### Data Type Gotchas

- **Compass NDJSON vs CSV**: NDJSON preserves data types (numbers, booleans, nulls) while CSV treats everything as strings. Use NDJSON for programmatic consumption, CSV for spreadsheet/ETL tools. [src6]
- **BOD date formats**: ION Connect BODs use ISO 8601 datetime formats, but some legacy Infor applications (LN, BAAN) store dates as integer offsets — transformation in ION Mapper is required. [src8]
- **.ionapi credential field names**: Older `.ionapi` files use `saession`/`sapassword` for service account keys; newer versions may use `saak`/`sask`. Code must handle both. [src4]

## Error Handling & Failure Points

### Common Error Codes

| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 401 Unauthorized | Token expired or invalid | Access token past 2-hour default lifetime | Refresh token and retry; check `.ionapi` credentials |
| 403 Forbidden | Insufficient permissions | Service account lacks role/permission for endpoint | Verify ION API authorized app has correct security roles in IFS |
| 404 Not Found | Endpoint not registered | API suite not deployed or wrong URL path | Check API suite registration in ION API admin; verify proxy context path |
| 408 Request Timeout | Gateway timeout exceeded | Request took longer than configured timeout (default 1 min) | Increase timeout (max 5 min) or switch to async pattern (Compass) |
| 429 Too Many Requests | Throttling/spikeArrest limit hit | Exceeded per-endpoint request rate | Implement exponential backoff; respect Retry-After header |
| 500 Internal Server Error | Backend application error | Target Infor application returned an error | Check ION API logs; verify backend application health |
| 502 Bad Gateway | Target unreachable | Backend Infor application is down or network issue | Check application server status; retry with backoff |
| 503 Service Unavailable | Gateway or backend overloaded | System under heavy load or maintenance | Wait and retry; check Infor Status page for planned maintenance |

[src1, src3]

### Failure Points in Production

- **Silent .ionapi credential rotation**: When the service account is regenerated in IFS, the existing `.ionapi` file becomes invalid but the old tokens may still work until expiry. Integration silently fails 2 hours later. Fix: `Monitor for 401 errors and trigger credential file reload`. [src3]
- **Compass results expire after ~20 hours**: If a nightly batch job queries Compass but doesn't retrieve results until the next business day, results are gone. Fix: `Retrieve and persist results immediately after query completion`. [src6]
- **Buffered policy 10 MB limit truncates large responses**: Endpoints with transformation policies silently truncate responses exceeding 10 MB. Fix: `Remove buffered policies for large-payload endpoints or paginate at the application level`. [src7]
- **Timeout mismatch between gateway and backend**: Gateway default is 1 minute, but some M3 programs take 2-3 minutes. The gateway kills the connection before the backend finishes. Fix: `Configure endpoint-level timeout extension to match expected backend duration (max 5 min)`. [src7]
- **Rate smoothing introduces unpredictable latency**: When `rateSmoothing.delayAfterCount` is exceeded, each subsequent request is artificially delayed. This makes P99 latencies spike without returning errors. Fix: `Monitor response times, not just error rates; implement client-side circuit breaker`. [src5]

## Anti-Patterns

### Wrong: Hardcoding the OAuth token endpoint URL

```python
# BAD — hardcoded token URL breaks when migrating between tenants or regions
TOKEN_URL = "https://inforos-us.inforcloudsuite.com/MYTENANT/as/token.oauth2"
```

### Correct: Extract token URL from .ionapi credentials file

```python
# 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
```

### Wrong: Polling Compass query status in a tight loop

```javascript
// 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;
}
```

### Correct: Poll with exponential backoff

```javascript
// 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(`${baseUrl}/v2/compass/jobs/${queryId}`, { headers });
  status = resp.data.status;
  delay = Math.min(delay * 2, 30000); // max 30s between polls
}
```

### Wrong: Ignoring the gateway timeout for long-running operations

```python
# BAD — large data export via REST times out at 60 seconds
response = requests.get(f"{base_url}/myapp/api/export-all-records",
    headers=headers, timeout=300)  # client timeout is 5 min but gateway kills at 1 min
```

### Correct: Use Compass V2 async queries for bulk data

```python
# GOOD — Compass handles long-running queries asynchronously
# Submit query (returns immediately)
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
```

## Common Pitfalls

- **Assuming a global rate limit exists**: Unlike Salesforce or Oracle, Infor does not publish a single rate limit number. Each API suite can have different throttling policies. Integration architects who assume "1,000 requests per minute" for all endpoints will be wrong for most of them. Fix: `Test each endpoint's throttling behavior independently or review the API suite configuration in ION API admin`. [src5]
- **Using the wrong OAuth grant type**: Resource Owner is correct for backend server-to-server. Using Authorization Code for batch jobs introduces unnecessary user interaction. Using Implicit in server contexts is insecure. Fix: `Resource Owner for backends, Authorization Code for web/mobile, SAML Bearer for Ming.le-embedded apps`. [src3]
- **Not handling credential file format differences**: The `.ionapi` file field names changed between versions (e.g., `saession`/`sapassword` vs `saak`/`sask`). Code that only handles one format breaks silently on different tenants. Fix: `Check for both field name variants with fallbacks`. [src4]
- **Treating Compass V2 like a real-time API**: Compass queries are async and results have a ~20-hour TTL. Using Compass for real-time lookups adds unnecessary latency and risks result expiration. Fix: `Use ION API Gateway REST endpoints for real-time; reserve Compass for analytics and bulk reads`. [src6]
- **Ignoring the 10 MB buffered policy limit**: API suites with SOAP-to-REST transformation policies silently truncate large payloads at 10 MB. This causes data loss without an error. Fix: `Avoid transformation policies for high-volume endpoints, or paginate at the application layer to stay under 10 MB per request`. [src7]

## Diagnostic Commands

```bash
# 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 (replace with actual 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 -X GET "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={access_token}&token_type_hint=access_token&client_id={clientId}&client_secret={clientSecret}"
```

## Version History & Compatibility

| Platform Release | Release Period | Status | Key Changes | Migration Notes |
|---|---|---|---|---|
| Infor OS 2025.x | 2025 H1 | Current | Compass V2 GA, enhanced throttling policies | Compass V1 deprecated — migrate to V2 offset/limit pagination |
| Infor OS 2024.x | 2024 | Supported | BaaS endpoint policies (Quota, Throttling, CacheResponse), OpenAPI 3.0.x support | New policy configuration format |
| Infor OS 2022.x | 2022 | Supported (limited) | SOAP-to-REST transformation via Handlebars, Swagger 2.0 registration | Older `.ionapi` credential file format |
| Infor OS 12.0.x | 2020-2021 | EOL | Initial Compass V1 APIs | Must upgrade to 2024.x+ for Compass V2 |

[src1, src2, src6]

### Deprecation Policy

Infor follows a release-based deprecation model tied to CloudSuite major versions. API features are typically supported for 2-3 release cycles after deprecation notice. Compass V1 APIs were deprecated with the introduction of V2 in 2024.x. Always check the Infor OS release notes for your specific CloudSuite product line. [src2]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Real-time REST API calls to Infor Landmark applications | High-volume bulk data 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 authentication across multiple Infor applications | Direct database access to Infor backend | Never bypass the 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 and auth) |
| Web/mobile apps needing user-context API access | File-based batch import/export (CSV/XML) | ION Mapper + ION Connect document flows |

## Important Caveats

- **Rate limits are NOT globally documented**: Infor's approach to throttling is per-endpoint configurable, not a single published number. This means integration planning requires testing or admin access to discover actual limits for each API suite. [src5]
- **Cloud vs on-premise are fundamentally different**: Cloud ION API Gateway (this card) uses OAuth 2.0 exclusively; on-premise ION Grid REST API v2 supports basic auth and client certificates. Do not mix documentation between the two. [src1]
- **Compass V2 is for Data Lake reads only**: Compass APIs are read-only SQL queries against the Infor Data Lake. They cannot write, update, or delete data in Infor applications. For write operations, use ION API Gateway REST endpoints or ION Connect BODs. [src6]
- **Tenant-specific URLs**: Every URL in this card contains `{region}` and `{tenant}` placeholders. These are unique per deployment and must be extracted from the `.ionapi` credentials file — never from documentation examples. [src3]
- **Information currency**: Infor updates ION API capabilities with each CloudSuite release. Rate limits, timeout values, and policy configurations are subject to change. Always verify against current release notes for your specific Infor OS version. [src1, src2]

## Related Units

- No directly related units currently published.
