# Basic information

## cairo.API

The cairo.ERP system provides its clients with API functionality based on JSON communication over the HTTP `POST`method. Unlike classical REST architecture, access to functions is done by invoking named operations passed in the request body. All data — both input and response — is transferred in JSON format.

The API allows parameters (such as session identifiers) to be sent in the request body, eliminating the need to pass them in the URL or headers.

<p class="callout info">XML communication is no longer supported</p>

All communication is done through a single endpoint:

POST [https://api.example.com/ws/&lt;method name&gt;](https://api.example.com/ws)

**Example: retrieving system version:**

```
POST /ws/getVersion HTTP/1.1
Host: api.example.com
Content-Type: application/json; charset=utf-8
SOAPAction: "getVersion"

{
  "getVersion": {}
}
```

Response:

```
{
  "getVersionResponse": {
    "ownum": "123",
    "verFull": "5.645.15127 2024-09-27",
    "version": "dbfd2 5.645.15127 2024-09-27 JSON"
  }
}
```

### Request structure

Every request should follow a consistent structure:

```
{
  "<operationName>": {
    <inputParameters>
  }
}
```

Example:

```
{
  "getProductsInfo": {
    "sessionId": "abc123",
    "productList": {
      "product": [{
          "id": "0006VI"
        }, {
          "reference": "144 666"
      }]
    }
  }
}

```

### Response structure

A valid response:

```
{
  "<operationName>Response": {
    <outputData>
  }
}
```

Example:

```
{
  "getVersionResponse": {
    "ownum": "123",
    "verFull": "5.645.15127 2024-09-27",
    "version": "dbfd2 5.645.15127 2024-09-27 JSON"
  }
}
```

### Error handling

In case of an error, the server returns an HTTP 500 response containing an `error` object in JSON format:

```
{
  "error": {
    "code": "ERR_SESSION",
    "msg": "Invalid session identifier"
  }
}
```

### Communication protocol

Access to the API is provided via an HTTP(S) connection to the server. All requests must include the header: `Content-Type: application/json; charset=utf-8`. Communication is stateless — sessions are identified via parameters passed in the request body.

### API access

To access the API, you need to know the target host and obtain API credentials. Please contact your account manager to obtain access data.

### Language version

By default, all messages returned from the API are in the language configured in the cairo.ERP system. To receive responses in a specific language, include the appropriate `languageId` during the `doLogin` method call.

## Session handling

Accessing API methods requires a successful login. Authentication is performed via the `doLogin` method, which requires a login and an MD5-encoded password. The `doLogin` method returns a unique session key (`sessionId`), which must be included in every subsequent API request.

### Example: login and session usage

Login request:

```
POST /ws/doLogin HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json

{
  "doLogin": {
    "userLogin": "test",
    "userPassword": "289dff07669d7a23de0ef88d2f7129e7"
  }
}
```

Response:

```
{
  "doLoginResponse": {
    "sessionId": "eP3cFozcI3pnyq9wO3Fa7vWg0H7CthI0029736_D"
  }
}
```

Subsequent request using session:

```
{
  "getProductsInfo": {
    "sessionId": "eP3cFozcI3pnyq9wO3Fa7vWg0H7CthI0029736_D",
    "productList": {
      "product": [
        { "id": "0006VI" },
        { "reference": "144 666" }
      ]
    }
  }
}
```

### Session lifecycle

- A session remains active for **10 minutes of inactivity**.
- The maximum lifetime of a session is **3 hours from login**, regardless of activity.
- After expiration, the server returns error code `ERR_SESSION`. In such cases, the doLogin method must be called again to obtain a new session key.
- Explicit logout is not required.

## Limits and restrictions

The API is subject to the following limits and constraints:

- **Concurrent sessions**: By default, a single application may have up to **30 active sessions**. Attempts to create additional sessions beyond this limit will result in an error.
- **Session timeout**: 
    - 10 minutes of inactivity,
    - 3 hours from login (absolute timeout).
- **Concurrent connections**: Each system installation has a limit on the number of concurrent API connections. The value of this limit depends on the system configuration and may be set individually.
- Exceeding available connections may result in requests being rejected until resources are released.

### PHP example

The following example demonstrates how to easily integrate with cairo.API using PHP. The `ws_call()`function serves as the main interface for WebService communication. It automatically handles session management, login, and session renewal in the event of an `ERR_SESSION` error.

Thanks to this, you can call any API method without manually managing session state — just call `ws_call()` with the method name and parameters.

```php
<?php

function ws_call($method, $params = [])
{
    static $sessionId = null;
    static $host = 'http://127.0.0.1:7888/';
    static $credentials = [
        'userLogin'    => 'test',
        'userPassword' => '289dff07669d7a23de0ef88d2f7129e7', // MD5 hash of password
    ];

    // Internal request function
    $call = function($method, $params) use ($host) {
        $request = [$method => $params];
        $requestStr = json_encode($request);
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $host,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $requestStr,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => true,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'Accept: application/json',
            ]
        ]);

        $responseStr = curl_exec($ch);
        $error       = curl_error($ch);
        $headersSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        curl_close($ch);

        if ($error) {
            echo "CURL ERROR: $error\n";
            return null;
        }

        $responseBody = substr($responseStr, $headersSize);
        $response = json_decode($responseBody, true);

        echo "REQUEST:\n$requestStr\n";
        echo "RESPONSE:\n$responseBody\n";

        return $response;
    };

    // Login if no session yet
    if ($sessionId === null && $method !== 'doLogin') {
        $loginResult = ws_call('doLogin', $credentials);
        if (!isset($loginResult['doLoginResponse']['sessionId'])) {
            echo "Authentication failed.\n";
            return null;
        }
        $sessionId = $loginResult['doLoginResponse']['sessionId'];
    }

    // Add sessionId to params
    if ($method !== 'doLogin') {
        $params['sessionId'] = $sessionId;
    }

    // Perform request
    $result = $call($method, $params);

    // Handle session expiration
    if (isset($result['error']['code']) && $result['error']['code'] === 'ERR_SESSION') {
        echo "Session expired, retrying login...\n";
        $sessionId = null;
        return ws_call($method, $params); // retry after re-login
    }

    return $result;
}

// ==========================
// Example usage: getProductsInfo
$productList = [
    'product' => [
        ['id' => '0006VI'],
        ['reference' => '144 666']
    ]
];

$response = ws_call('getProductsInfo', ['productList' => $productList]);

if ($response) {
    print_r($response);
} else {
    echo "Request failed.\n";
}
```