# Overview

## Idempotency

The HTTP `idempotency-key` request header field can be used to carry idempotency key in order to make non-idempotent HTTP methods such as `POST` or `PATCH` fault-tolerant

Client can make the same request repeatedly because of network error, timeout etc.. but the same operation will not be executed twice. If Client repeats the same request by sending the same Idempotency Key value in request headers, and the request will be responded with the same response as the original request.&#x20;

Name of the request header is `idempotency-key`.

{% hint style="info" %}
Usage of Idempotency is optional.
{% endhint %}

## Security

### x-api-key

The HTTP `x-api-key` request header field must be used to identify Realm.

### Authorization

Add `Authorization` request header with `x-api-key` value.

### Authentication

To get x-api-key for Orangepill Admin API or Orangepill API send authentication data to `/auth/login` endpoint.

#### Orangepill Admin API Login

Use your email and password of Orangepill Realm [Admin](https://docs.orangepill.cloud/realms-and-admins#setup-realm-admin-user).

{% tabs %}
{% tab title="cURL" %}

```shell
curl --location --request POST 'https://admin.orangepill.cloud/v1/auth/admins/login' \
--header 'x-api-key: AXVubzpwQDU1dzByYM==' \
--header 'Content-Type: application/json' \
--data-raw '{
    "realm_key": "0dd4f142-f6de-4657-a521-352c9e17fcbe",
    "email": "your@email.com",
    "password": "your_password"
}'
```

{% endtab %}
{% endtabs %}

in response you will receive `x-api-key`.

{% tabs %}
{% tab title="Response" %}

```json
{
    "x-api-key": "1NHSjRWMWd5YkdkR2FWRm5iME1MjJjZGE0MTYtMjA4Yy00MTJjLThjMzgtOTcwNzhiMGQ3ODA2OnRvbWlzbGF2LmJpc2NhbkBnbWFpbC5jb206Y21oM1QyYzFkRlZ4WjFkNVFqUXhWMHd5UmtNMFJ6VnZXVm81YUZvd4ZDNnMVRYWk5SQ1JUSkE9PQ=="
}
```

{% endtab %}
{% endtabs %}

#### Orangepill API Login

Use Realm key, Username and Password of Orangepill Realm [User](https://docs.orangepill.cloud/identities-and-users#create-user).

{% tabs %}
{% tab title="cURL" %}

```shell
curl --location --request POST 'https://api.orangepill.cloud/v1/auth/admins/login' \
--header 'x-api-key: AXVubzpwQDU1dzByYM==' \
--header 'Content-Type: application/json' \
--data-raw '{
    "realm_key": "0dd4f142-f6de-4657-a521-352c9e17fcbe",
    "username": "your_username",
    "password": "your_password"
}'
```

{% endtab %}
{% endtabs %}

in response you will receive `x-api-key`.

{% tabs %}
{% tab title="Response" %}

```json
{
    "x-api-key": "1NHSjRWMWd5YkdkR2FWRm5iME1MjJjZGE0MTYtMjA4Yy00MTJjLThjMzgtOTcwNzhiMGQ3ODA2OnRvbWlzbGF2LmJpc2NhbkBnbWFpbC5jb206Y21oM1QyYzFkRlZ4WjFkNVFqUXhWMHd5UmtNMFJ6VnZXVm81YUZvd4ZDNnMVRYWk5SQ1JUSkE9PQ=="
}
```

{% endtab %}
{% endtabs %}

Add `x-api-key` header to every HTTP call.

### Ownership

Each created entity is flagged with `owner: authenticated_user.id` thus enabling automatic scope filtering and ownership.

### Roles

User can have different roles in a realm.

<table><thead><tr><th width="183.5">Role</th><th width="553.5">Description</th></tr></thead><tbody><tr><td><code>admin</code></td><td>Create and update users and entities. Can manage deleted entities.</td></tr><tr><td><code>manage</code></td><td>Create and update users and entities.</td></tr><tr><td><code>user</code></td><td>Create and update entities.</td></tr></tbody></table>

### Scopes

User is limited in it's scope to access data.

User scopes:

<table><thead><tr><th width="188.66666666666666">Scope</th><th width="501">Description</th></tr></thead><tbody><tr><td><code>realm</code></td><td>Access to realm entities.</td></tr><tr><td><code>own</code></td><td>Access to own entities.</td></tr></tbody></table>

### Cumulative Permissions

<table><thead><tr><th width="196">Role, Scope</th><th>Permission</th></tr></thead><tbody><tr><td><code>user,own</code></td><td>Can read owned entities.<br>Can write owned entities. <br>Can delete owned entities. <br>Cannot delete owned user. <br>Cannot undelete.<br>Cannot create users.</td></tr><tr><td><code>user,realm</code></td><td>Can read all entities.<br>Can write owned entities. <br>Can delete owned entities. <br>Cannot delete owned user. <br>Cannot undelete. <br>Cannot create users.</td></tr><tr><td><code>manage,own</code></td><td>Can read owned entities.<br>Can write owned entities. <br>Can delete owned entities. <br>Can delete owned user. <br>Can undelete owned.<br>Cannot create users.</td></tr><tr><td><code>manage,realm</code></td><td>Can read all entities.<br>Can update all entities.<br>Can delete all entities.<br>Can delete all users.<br>Can undelete all. <br>Cannot create users.</td></tr><tr><td><code>admin,own</code></td><td>Can read owned entities.<br>Can write owned entities.<br>Can delete own entities.<br>Can delete own user.<br>Can undelete own.<br>Can create users.</td></tr><tr><td><code>admin,realm</code></td><td>Can read all entities.<br>Can write all entities.<br>Can delete all entities.<br>Can delete all users.<br>Can undelete all.<br>Can create users.</td></tr></tbody></table>

### API scopes

When calling API you can apply following scopes on entities.

<table><thead><tr><th width="245.66666666666666">Scope</th><th width="249.33333333333337">Description</th></tr></thead><tbody><tr><td><code>own (default)</code></td><td>Entities where current user is owner.</td></tr><tr><td><code>all</code></td><td>All realm entities.</td></tr><tr><td><code>incoming</code></td><td>Applies only for transactions. Transactions where current user is owner of destination account.</td></tr><tr><td><code>outgoing</code></td><td>Applies only for transactions. Transactions where current user is owner of source account.</td></tr><tr><td><code>deleted</code></td><td>Deleted entities.</td></tr></tbody></table>

## Soft delete

Entities are never physically deleted, hence soft delete mechanism is applied. When `DELETE` method is invoked on REST API interface, Orangepill middleware will flag the entity as `deleted: true`,  and timestamped as `deleted_at: Date.now()`.  Entities marked as deleted are filtered out from **scopes** `all` and `own`. To view deleted entities either disable **scopes** by adding `?scope=false` to URL query or choose `?scope=deleted` if exists for specific endpoint.

## RESTful

Orangepill API is completely RESTful thus being interface for buidling frontend and backend apps.

### `find` Find entities

Find entitites by query.

#### Parameters

<table><thead><tr><th width="178">Property</th><th>Type</th><th width="91">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>limit</code></td><td><code>Number</code></td><td><code>null</code></td><td>Max count of rows.</td></tr><tr><td><code>offset</code></td><td><code>Number</code></td><td><code>null</code></td><td>Number of skipped rows.</td></tr><tr><td><code>fields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields to return.</td></tr><tr><td><code>sort</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Sorted fields.</td></tr><tr><td><code>search</code></td><td><code>String</code></td><td><code>null</code></td><td>Search text.</td></tr><tr><td><code>searchFields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields for search.</td></tr><tr><td><code>scope</code></td><td><code>String|Array&#x3C;String>|Boolean</code></td><td><code>null</code></td><td>Scopes for the query. If <code>false</code>, the default scopes are disabled.</td></tr><tr><td><code>populate</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Populated fields.</td></tr><tr><td><code>query</code></td><td><code>String|Object</code></td><td><code>null</code></td><td>Query object. If <code>String</code>, it will be converted with <code>JSON.parse</code></td></tr></tbody></table>

#### REST endpoint

```
GET {serviceName}/all
```

#### Results

```
[
    {
        id: "akTRSKTKzGCg9EMz",
        ...
    },
    {
        id: "0YZQR0oqyjKILaRn",
        ...
    }
]
```

### `list` List entities

List entities with pagination. It returns also the total number of rows.

#### Parameters

<table><thead><tr><th width="176">Property</th><th>Type</th><th width="89">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>page</code></td><td><code>Number</code></td><td><code>null</code></td><td>Page number.</td></tr><tr><td><code>pageSize</code></td><td><code>Number</code></td><td><code>null</code></td><td>Size of a page.</td></tr><tr><td><code>fields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields to return.</td></tr><tr><td><code>sort</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Sorted fields.</td></tr><tr><td><code>search</code></td><td><code>String</code></td><td><code>null</code></td><td>Search text.</td></tr><tr><td><code>searchFields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields for search.</td></tr><tr><td><code>scope</code></td><td><code>String|Array&#x3C;String>|Boolean</code></td><td><code>null</code></td><td>Scopes for the query. If <code>false</code>, the default scopes are disabled. Example: <code>?scope=-own,all</code> removes own scope, and adds all.</td></tr><tr><td><code>populate</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Populated fields.</td></tr><tr><td><code>query</code></td><td><code>String|Object</code></td><td><code>null</code></td><td>Query object. If <code>String</code>, it's converted with <code>JSON.parse</code></td></tr></tbody></table>

#### REST endpoint

```
GET {serviceName}
```

#### Results

```
{
    rows: [
        {
            id: "2bUwg4Driim3wRhg",
            ...,
        },
        {
            id: "Di5T8svHC9nT6MTj",
            ...,
        },
        {
            id: "YVdnh5oQCyEIRja0",
            ...,
        },
    ],
    total: 3,
    page: 1,
    pageSize: 10,
    totalPages: 1,
}
```

### `count` Count entities

Get the number of entities by query.

#### Parameters

<table><thead><tr><th width="174">Property</th><th>Type</th><th width="94">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>search</code></td><td><code>String</code></td><td><code>null</code></td><td>Search text.</td></tr><tr><td><code>searchFields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields for search.</td></tr><tr><td><code>scope</code></td><td><code>String|Array&#x3C;String>|Boolean</code></td><td><code>null</code></td><td>Scopes for the query. If <code>false</code>, the default scopes are disabled.</td></tr><tr><td><code>query</code></td><td><code>String|Object</code></td><td><code>null</code></td><td>Query object. If <code>String</code>, it's converted with <code>JSON.parse</code></td></tr></tbody></table>

#### REST endpoint

```
GET {serviceName}/count
```

#### Results

```
15
```

### `get` Get an entity by ID

Get an entity by ID.

#### Parameters

<table><thead><tr><th width="134">Property</th><th>Type</th><th width="97">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>&#x3C;id></code></td><td><code>any</code></td><td><code>null</code></td><td>ID of the entity. The name of the property comes from the primary key field.</td></tr><tr><td><code>fields</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Fields to return.</td></tr><tr><td><code>scope</code></td><td><code>String|Array&#x3C;String>|Boolean</code></td><td><code>null</code></td><td>Scopes for the query. If <code>false</code>, the default scopes are disabled.</td></tr><tr><td><code>populate</code></td><td><code>String|Array&#x3C;String></code></td><td><code>null</code></td><td>Populated fields.</td></tr></tbody></table>

#### REST endpoint

```
GET {serviceName}/{id}
```

#### Results

```
{
    id: "YVdnh5oQCyEIRja0",
    ...,
}
```

##
