REST Integration Skill
SkillDocs & knowledgeTeaches your agent how to call external REST APIs from Mendix and pick the right integration approach.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the REST Integration Skill skill
About this capability
Call external REST APIs from Mendix, the three approaches (inline REST CALL, consumed REST client document, generated from OpenAPI) and how to choose. Use when integrating with any external REST API.
What this skill tells your AI
The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/rest-client/SKILL.md and read by ahel’s review.
Use this skill when integrating with external REST APIs from Mendix.
Three Approaches
Mendix offers three ways to call REST APIs from microflows. Choose based on the use case:
| Approach | When to Use | Artifacts |
|---|---|---|
| OpenAPI import | API has an OpenAPI 3.0 spec — auto-generate from the spec | REST client document generated in one command |
| REST Client (manual) | No spec available, or need fine-grained control | REST client document + microflow |
| REST CALL (inline) | One-off calls, quick prototyping, dynamic URLs, low-level HTTP control | Microflow only |
Both REST Client approaches can be combined with Data Transformers (Mendix 11.9+) and Import/Export Mappings to map between JSON and entities.
No API to call against yet — or one you would rather not depend on while building? mock-rest-apis covers standing up an endpoint you control and pointing the app at it.
Approach 0: OpenAPI Import (Fastest)
If the API has an OpenAPI 3.0 spec (JSON or YAML), generate the REST client in one command:
-- From a local file (relative to the .mpr file)
create or modify rest client CapitalModule.CapitalAPI (
OpenAPI: 'specs/capital.json'
);
-- From a URL
create or modify rest client PetStoreModule.PetStoreAPI (
OpenAPI: 'https://petstore3.swagger.io/api/v3/openapi.json'
);
-- Override the base URL (replaces servers[0].url from the spec)
create or modify rest client PetStoreModule.PetStoreStaging (
OpenAPI: 'https://petstore3.swagger.io/api/v3/openapi.json',
BaseUrl: 'https://staging.petstore.example.com/api/v3'
);
This generates:
- All operations with correct HTTP method, path, parameters, headers, body, and response type
- Resource groups based on OpenAPI
tags - Basic auth if the spec declares it at the top level
- The spec stored inside the document for Studio Pro parity
BaseUrl is optional. When omitted, servers[0].url from the spec is used — but only if it is
absolute. A relative server URL (/api/v3) cannot be a BaseUrl: the import warns
("server URL … is relative and cannot be used as BaseUrl") and leaves the client without one,
which fails at call time rather than at import time. Set BaseUrl explicitly in that case.
When provided, BaseUrl overrides the spec's value — useful when the spec points at production
but you need to import against staging, a different version, or a local mock
(see mock-rest-apis).
Preview without writing:
describe contract operation from openapi 'specs/capital.json';
After import: the REST client is ready to use with SEND REST REQUEST. No manual operation definition needed.
Approach 1: REST Client (Manual)
Define the API once as a REST client document, then call its operations from microflows.
Step 1 — Create the REST Client
create rest client Module.OpenMeteoAPI (
BaseUrl: 'https://api.open-meteo.com/v1',
authentication: none
)
{
operation GetForecast {
method: get,
path: '/forecast',
query: ($latitude: decimal, $longitude: decimal, $current: string),
headers: ('Accept' = 'application/json'),
timeout: 30,
response: json as $WeatherJson
}
operation PostData {
method: post,
path: '/submit',
headers: ('Content-Type' = 'application/json'),
body: json from $JsonPayload,
response: none
}
};
Authentication
-- No authentication
authentication: none
-- Basic auth
authentication: basic (username: 'user', password: 'secret')
Body Types
-- JSON variable (Mendix expression stored on the operation)
body: json from $JsonPayload
-- String template with parameter placeholders
body: template '{ "name": "{name}", "value": {value} }'
-- Export mapping (entity → JSON, aligned with export mapping syntax)
body: mapping Module.RequestEntity {
name = Name,
email = Email,
}
There is no binary/file body here. A consumed operation's body is one of
Rest$JsonBody, Rest$StringBody or Rest$ImplicitMappingBody, so a file
document has nowhere to go. body: file from $Doc is refused as MDL-REST02
— it used to be written as a string body holding the literal text $Doc, which
returns HTTP 200 with a 4-byte payload.
Upload binary from a microflow instead, which does have a binary body:
rest call post 'https://api.example.com/upload'
header 'ContentType' = 'application/pdf'
body binary $Doc/Contents
returns response;
response: file as $Doc on an operation is unaffected — downloads work.
Response Types
-- Simple types (variable binding is at the call site, not stored on the operation)
response: json as $Result
response: string as $text
response: file as $Document
response: status as $Code
response: none
-- Import mapping (JSON → entity, aligned with import mapping syntax)
response: mapping Module.ResponseEntity {
"Id" = "id",
"status" = "status",
create Module.Items_Response/Module.Item = items {
"Sku" = "sku",
"Quantity" = "quantity",
}
}
mapping takes an ENTITY plus a body — never a mapping document. The name
after mapping is the target entity, and the { ... } body lists the JSON
fields; Mendix stores the result inline on the operation
(Rest$ImplicitMappingResponseHandling). A consumed REST operation has nowhere
to put a reference to a standalone import/export mapping — the metamodel defines
only the inline handler and "no response handling". So this is wrong, and is
now rejected by mxcli check as MDL-REST01:
response: mapping Module.IMM_Something -- ✗ names a mapping document, no body
If you already have an import mapping document you want to reuse, call it from
the microflow instead — send rest request with response: json as $Raw, then
$Obj = import from mapping Module.IMM_Something($Raw).
Reaching a nested JSON member
A member several levels down is written with /, exactly as in a mapping
document — you do NOT need an entity for the levels in between:
response: mapping Module.FlatProbe {
"ItemId" = "id",
"Title" = "fields/Title", -- reaches {"fields": {"Title": …}}
}
Mendix stores that as the pipe path (Object)|fields|Title. Before mxcli
0.19.x the inline serializer appended the text verbatim, producing
(Object)|fields/Title — one member with a slash in its name — which passed
mxcli check, exec, mx check and describe, and returned an empty
column at runtime with no diagnostic. If you are on an older build, give each
level its own entity instead.
An export body's nested elements
Body: MAPPING is an EXPORT mapping, and its nested elements must not be
"create". mxcli writes them correctly, but it is worth knowing what a wrong one
looks like, because mxbuild does not report it as a check error — it throws
while loading the project, so the app cannot be opened at all:
System.AggregateException: … (Export Object Mappings cannot have
ObjectHandling set to 'Create')
Query parameters carry no type in the Mendix model: Rest$QueryParameter stores
a name only. MDL still requires $name: Type for the sake of the grammar, but
the type is dropped at write time, so describe re-emits every query parameter
as String.
Step 2 — Call from a Microflow
create microflow Module.ACT_GetWeather ()
returns Module.WeatherInfo as $Weather
begin
-- Call the REST client operation
send rest request Module.OpenMeteoAPI.GetForecast;
-- Extract response body from system variable
declare $RawJson string = $latestHttpResponse/content;
-- (Optional) Transform with JSLT
$SimplifiedJson = transform $RawJson with Module.SimplifyWeather;
-- Import into entity
$Weather = import from mapping Module.IMM_Weather($SimplifiedJson);
return $Weather;
end;
/
CRITICAL: After send rest request, the response is in $latestHttpResponse (System.HttpResponse):
$latestHttpResponse/content— response body (String)$latestHttpResponse/StatusCode— HTTP status (Integer)
Show / Describe / Drop
show rest clients [in module];
describe rest client Module.ClientName;
drop rest client Module.ClientName;
create or modify rest client Module.ClientName ... -- idempotent
Approach 2: REST CALL (Inline HTTP)
Call an HTTP endpoint directly from a microflow — no REST client document needed. Best for one-off calls, dynamic URLs, or low-level control.
-- Simple GET returning a string
$response = rest call get 'https://api.example.com/data'
header Accept = 'application/json'
timeout 30
returns string;
-- GET with URL template parameters
$response = rest call get 'https://api.example.com/users/{1}' with (
{1} = toString($UserId)
)
header Accept = 'application/json'
returns string;
-- POST with body
$response = rest call post 'https://api.example.com/items'
header 'Content-Type' = 'application/json'
body '{"name": "test"}'
returns string;
-- With basic auth
$response = rest call get 'https://api.example.com/secure'
auth basic 'username' password 'password'
returns string;
-- With import mapping (JSON → entity)
$item = rest call get 'https://api.example.com/item/1'
header Accept = 'application/json'
returns mapping Module.IMM_Item as Module.Item;
-- Fire and forget
rest call delete 'https://api.example.com/item/1'
returns nothing;
-- Error handling
$response = rest call get 'https://api.example.com/data'
returns string
on error continue;
Data Transformers (JSLT — Mendix 11.9+)
Transform complex JSON responses into simpler structures before import mapping.
-- Define the transformer
create data transformer Module.SimplifyWeather
source json '{"latitude": 52.52, "current": {"temperature_2m": 12.8, "wind_speed_10m": 18.3}}'
{
jslt $$
{
"temp": .current.temperature_2m,
"wind": .current.wind_speed_10m,
"lat": .latitude
}
$$;
};
-- Use in a microflow
$SimplifiedJson = transform $RawJson with Module.SimplifyWeather;
Single-line JSLT: jslt '{ "temp": .current.temperature_2m }';
Multi-line JSLT: jslt $$ { ... } $$; (dollar-quoting, same as Java actions)
list data transformers [in module];
describe data transformer Module.Name;
drop data transformer Module.Name;
JSON Structures & Mappings
See json-structures-and-mappings for full reference. Quick summary:
-- JSON structure from snippet
create json structure Module.JSON_Weather
snippet '{"temp": 12.8, "wind": 18.3, "lat": 52.52}';
-- Non-persistent entity
create non-persistent entity Module.WeatherInfo (
Temperature: decimal,
WindSpeed: decimal,
Latitude: decimal
);
/
-- Import mapping (JSON → entity)
create import mapping Module.IMM_Weather
with json structure Module.JSON_Weather
{
create Module.WeatherInfo {
Temperature = temp,
WindSpeed = wind,
Latitude = lat
}
};
-- Use in microflow
$Weather = import from mapping Module.IMM_Weather($JsonString);
$JsonOutput = export to mapping Module.EMM_Weather($WeatherEntity);
Complete Pipeline Example
Full example: call weather API → transform → import → show on page.
-- 1. Entity
create non-persistent entity Module.CurrentWeather (
Temperature: decimal,
WindSpeed: decimal,
Latitude: decimal,
ObservationTime: datetime
);
/
-- 2. Data Transformer (simplify API response)
create data transformer Module.SimplifyWeather
source json '{"latitude":52.52,"current":{"time":"2024-01-15T14:00","temperature_2m":12.8,"wind_speed_10m":18.3}}'
{
jslt $$
{
"temperature": .current.temperature_2m,
"windSpeed": .current.wind_speed_10m,
"latitude": .latitude,
"observationTime": .current.time
}
$$;
};
-- 3. JSON Structure + Import Mapping (for transformed output)
create json structure Module.JSON_Weather
snippet '{"temperature":12.8,"windSpeed":18.3,"latitude":52.52,"observationTime":"2024-01-15T14:00"}';
create import mapping Module.IMM_Weather
with json structure Module.JSON_Weather
{
create Module.CurrentWeather {
Temperature = temperature,
WindSpeed = windSpeed,
Latitude = latitude,
ObservationTime = observationTime
}
};
-- 4. REST Client
create rest client Module.WeatherAPI (
BaseUrl: 'https://api.open-meteo.com/v1',
authentication: none
)
{
operation GetCurrent {
method: get,
path: '/forecast',
query: ($latitude: decimal, $longitude: decimal, $current: string),
headers: ('Accept' = 'application/json'),
response: json as $Result
}
};
-- 5. Microflow (REST Client → Transform → Import)
create microflow Module.ACT_GetWeather ()
returns Module.CurrentWeather as $Weather
begin
send rest request Module.WeatherAPI.GetCurrent;
declare $RawJson string = $latestHttpResponse/content;
$SimplifiedJson = transform $RawJson with Module.SimplifyWeather;
$Weather = import from mapping Module.IMM_Weather($SimplifiedJson);
return $Weather;
end;
/
Mendix Validation Rules
| Rule | Error | Fix |
|---|---|---|
| Every operation MUST have an Accept header | CE7062 | Auto-added by serializer if missing |
| POST/PUT/PATCH MUST have a body | CE7064 | Auto-added by serializer (empty JSON body) |
| Template placeholders must match parameters | CE7056 | {name} requires a parameter named name |
| No custom error handling on SEND REST REQUEST | CE6035 | Always uses abort-on-error semantics |
| Data Transformer requires 11.9+ | version check | checkFeature("integration", "data_transformer", ...) |
BSON Types Reference
| MDL Concept | BSON Type |
|---|---|
| REST client document | rest$ConsumedRestService |
| Operation | rest$RestOperation |
| GET/DELETE method | rest$RestOperationMethodWithoutBody |
| POST/PUT/PATCH method | rest$RestOperationMethodWithBody |
| JSON body | rest$JsonBody |
| Template body | rest$StringBody |
| Export mapping body | rest$ImplicitMappingBody |
| No response | rest$NoResponseHandling |
| Import mapping response | rest$ImplicitMappingResponseHandling |
| Header | rest$HeaderWithValueTemplate |
| Path parameter | rest$OperationParameter |
| Query parameter | rest$QueryParameter |
| Data transformer | DataTransformers$DataTransformer |
| JSLT step | DataTransformers$JsltAction |
| Transform action | microflows$TransformJsonAction |
| Send request action | microflows$RestOperationCallAction |
| Inline HTTP action | microflows$RestCallAction |
Signals
- GitHub stars
- 122
- Forks
- 49
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
rest-client- Source
- github.com/mendixlabs/mxcli