External ERP Integration API
MindAeon provides a tenant-scoped REST API for server-to-server (machine-to-machine) integration with SAP, Logo, Mikro, Netsis and all enterprise systems. Authentication uses the OAuth2 client_credentials flow; all public endpoints are versioned under /api/v1/* Follow the 3 steps below.
1 Create an API client
In MindAeon Web, go to Integration → API Clients → New Client Enter the client name, select the required permissions (scope) and create it. The Client Secret is shown only once — save it somewhere safe. Only its hash is stored.
| Scope | Grants |
|---|---|
products:write | Product upsert |
products:read | Product read |
customers:write | Account upsert |
warehouses:write | Warehouse upsert |
stock:read | Stock query |
2 Get a token
POST /api/v1/auth/token — obtain a short-lived (30 min) access token with your client credentials. Machine clients do not receive a refresh token; request a new token when it expires.
curl -X POST "https://mobil.androidsahasatis.com/api/v1/auth/token" \
-H "Content-Type: application/json" \
-d '{
"grantType": "client_credentials",
"companyCode": "DEMO",
"clientId": "mac_x8Kd...",
"clientSecret": "P7f...=="
}'
Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 1800,
"scope": "products:write customers:write stock:read"
}
companyCode is your company (tenant) code. Invalid credentials / inactive / expired client → 401 { "error": "invalid_client" }.3 Make a call
Send the Authorization: Bearer <accessToken> header on every request.
POST Product upsert (bulk, idempotent)
externalCode is the matching key (or code ); a second submission with the same externalCode counts as an update (duplicate-safe).
curl -X POST "https://mobil.androidsahasatis.com/api/v1/products:upsert" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "externalCode":"ERP-1001", "code":"URN0001", "name":"Kalem",
"stockType":"Product", "trackingType":"None",
"gtin":"8690000000001", "salesVatRate":20, "isActive":true }
]'
# → { "created":1, "updated":0, "failed":0, "errors":[] }
POST Account / Warehouse upsert
POST https://mobil.androidsahasatis.com/api/v1/customers:upsert # Customer.Edit / customers:write POST https://mobil.androidsahasatis.com/api/v1/warehouses:upsert # Warehouse.Edit / warehouses:write
GET Stock query (multi-warehouse)
curl "https://mobil.androidsahasatis.com/api/v1/stock?warehouseCode=WH01&productCode=URN0001" \
-H "Authorization: Bearer $TOKEN"
# → { "asOf":"...", "items":[
# { "warehouseCode":"WH01","warehouseName":"Merkez Depo",
# "productCode":"URN0001","productName":"Kalem",
# "unitCode":"ADET","quantity":150.0 } ] }
quantity = the net total of stock movements (in − out).Code Samples (Languages)
The same flow (get token → product upsert) in different languages. clientId/clientSecret should be replaced with your own client credentials, and DEMO with your company code.
TOKEN=$(curl -s -X POST "https://mobil.androidsahasatis.com/api/v1/auth/token" \
-H "Content-Type: application/json" \
-d '{"grantType":"client_credentials","companyCode":"DEMO","clientId":"mac_...","clientSecret":"..."}' \
| sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
curl -X POST "https://mobil.androidsahasatis.com/api/v1/products:upsert" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '[{"externalCode":"ERP-1001","code":"URN0001","name":"Kalem","stockType":"Product","isActive":true}]'
using System.Net.Http.Json;
var http = new HttpClient();
var tokenResp = await http.PostAsJsonAsync("https://mobil.androidsahasatis.com/api/v1/auth/token", new {
grantType = "client_credentials", companyCode = "DEMO",
clientId = "mac_...", clientSecret = "..." });
var auth = await tokenResp.Content.ReadFromJsonAsync<TokenResp>();
http.DefaultRequestHeaders.Authorization = new("Bearer", auth!.accessToken);
var res = await http.PostAsJsonAsync("https://mobil.androidsahasatis.com/api/v1/products:upsert", new[] {
new { externalCode = "ERP-1001", code = "URN0001", name = "Kalem", stockType = "Product", isActive = true }
});
Console.WriteLine(await res.Content.ReadAsStringAsync());
record TokenResp(string accessToken, string tokenType, int expiresIn, string scope);
import requests
api = "https://mobil.androidsahasatis.com"
tok = requests.post(f"{api}/api/v1/auth/token", json={
"grantType": "client_credentials", "companyCode": "DEMO",
"clientId": "mac_...", "clientSecret": "..."}).json()
headers = {"Authorization": f"Bearer {tok['accessToken']}"}
res = requests.post(f"{api}/api/v1/products:upsert", headers=headers, json=[
{"externalCode": "ERP-1001", "code": "URN0001", "name": "Kalem",
"stockType": "Product", "isActive": True}])
print(res.json())
const api = "https://mobil.androidsahasatis.com";
const tok = await (await fetch(`${api}/api/v1/auth/token`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ grantType: "client_credentials", companyCode: "DEMO",
clientId: "mac_...", clientSecret: "..." })
})).json();
const res = await fetch(`${api}/api/v1/products:upsert`, {
method: "POST",
headers: { "Authorization": `Bearer ${tok.accessToken}`, "Content-Type": "application/json" },
body: JSON.stringify([{ externalCode: "ERP-1001", code: "URN0001", name: "Kalem",
stockType: "Product", isActive: true }])
});
console.log(await res.json());
<?php
$api = "https://mobil.androidsahasatis.com";
$authBody = json_encode(["grantType"=>"client_credentials","companyCode"=>"DEMO",
"clientId"=>"mac_...","clientSecret"=>"..."]);
$tok = json_decode(file_get_contents("$api/api/v1/auth/token", false, stream_context_create([
"http"=>["method"=>"POST","header"=>"Content-Type: application/json","content"=>$authBody]])), true);
$body = json_encode([["externalCode"=>"ERP-1001","code"=>"URN0001","name"=>"Kalem",
"stockType"=>"Product","isActive"=>true]]);
echo file_get_contents("$api/api/v1/products:upsert", false, stream_context_create([
"http"=>["method"=>"POST",
"header"=>"Authorization: Bearer {$tok['accessToken']}\r\nContent-Type: application/json",
"content"=>$body]]));
Live reference
Use the Scalar reference for the schema of all endpoints, sample bodies, and a "try it" interface. The OpenAPI 3 document is generated in every environment.
Scalar API Reference → openapi/v1.json
Security notes
- The token is tenant-scoped ; it can access only the data of your client's company.
- Access is limited to the scopes you select (double gate: license module + permission). A call outside scope →
403. - The Client Secret is stored in the system as only a hash (PBKDF2/SHA256, constant-time verification).
- If the client is revoked (
IsActive=false) or expires, no new token can be issued; any issued token expires at its natural lifetime (≤30 min).
Licensing & Roles
- API access is via role/scope-defined clients: each client is granted only the required permissions (scope); an out-of-scope call receives
403. - Every active API client counts toward the number of users in the license (it is chargeable). When creating a new API client, the total of active users + active API clients cannot exceed the license user limit; if exceeded, creation is blocked and you are asked to raise the limit.
- Admin/system-management endpoints are closed to the API; the API accesses only business-module data (read/write).