# The REST API

For developers

GrandNode ships an HTTP API in the `Grand.Module.Api` module. It has two parts with separate configuration and separate tokens: the **Backend API** (`/api/...`) for integrations that manage the catalog and customers — ERP, PIM, import jobs — and the **Frontend API**, which exposes the storefront's own actions (catalog, cart, checkout, account) to a headless or mobile frontend. Both use JSON Web Tokens and describe themselves with OpenAPI.

### How to enable it

The API module is switched off by default. In `App_Data/appsettings.json` (or with environment variables):

```
"FeatureManagement": {
  "Grand.Module.Api": true
},
"BackendAPI": {
  "Enabled": true,
  "SecretKey": "a-long-random-secret-of-at-least-32-characters",
  "ExpiryInMinutes": 1440
},
"FrontendAPI": {
  "Enabled": true,
  "SecretKey": "another-long-random-secret-of-32-characters-or-more",
  "JsonContentType": false,
  "ExpiryInMinutes": 1440,
  "RefreshTokenExpiryInMinutes": 1440
}
```

- `FeatureManagement:Grand.Module.Api` loads the module at all. Restart the application after changing it.
- `SecretKey` signs the tokens. Outside the Development environment the application refuses to start while an enabled API has an empty key, the shipped placeholder or a key shorter than 32 characters. Keep it out of source control: set `BackendAPI__SecretKey` and `FrontendAPI__SecretKey` as environment variables or in a secret store.
- In production, cross-origin requests are allowed only from the origins listed in `AllowedHostOrigins`. In Development any origin is allowed.

### API documentation

In the Development environment the module generates OpenAPI documents at `/openapi/v1.json` (Backend API) and `/openapi/v2.json` (Frontend API), plus an interactive Scalar playground at `/scalar/v1` and `/scalar/v2` where you can authorise with a token and try every endpoint. Production instances do not publish the documents, so explore the API on a development copy. A step-by-step guide with all settings: [API configuration and the Scalar playground](https://grandnode.com/developers-scalar-api-playground).

### Backend API: API users and tokens

Backend API access is granted per API user, managed in the admin panel under **System → Developer Tools → Manage api users**.

![Manage api users page in the admin panel with the Add new record button and the Email, Password and Is active columns](https://grandnode.com/Plugins/Theme.GrandNodeCom/Content/kb/developers/admin-api-users.webp)

System → Developer Tools → Manage api users. Each record has an e-mail, a password and an active flag.

1. Make sure a customer account with the same e-mail exists, is active and belongs to a customer group with access to the admin panel. The API user acts with that customer's permissions — for example, product endpoints require the *Products* permission.
2. Click **Add new record**, enter the e-mail and a password, tick **Is active** and save.
3. Request a token. The password is sent Base64-encoded:
   ```
   POST /Api/Token/Create
   Content-Type: application/json

   { "email": "erp@example.com", "password": "c2VjcmV0LXBhc3N3b3Jk" }
   ```
   The response is the token as a JSON string.
4. Send it with every call:
   ```
   GET /api/Product?$filter=Published == true&$top=20
   Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
   ```

A token is accepted only while the API user is active and its password unchanged: setting a new password or clearing **Is active** invalidates every token issued before. When **Admin area allowed IP** is set under **Settings → General**, API calls from other addresses are refused too.

### Backend API endpoints

Controllers are available for brands, categories, collections and their layouts, products and product layouts, product attributes, specification attributes, pictures, customers, customer groups, vendors, stores, languages, currencies, countries, shipping methods, delivery dates, warehouses and pickup points. They follow one pattern, shown here for products:

| Request | What it does |
|---|---|
| `GET /api/Product` | List, with query options |
| `GET /api/Product/{key}` | One product by id |
| `POST /api/Product` | Create |
| `PUT /api/Product` | Replace |
| `PATCH /api/Product/{key}` | Partial update with a JSON Patch document |
| `DELETE /api/Product/{key}` | Delete |
| `POST /api/Product/{key}/UpdateStock` | Stock update; similar actions manage the product's categories, collections, pictures, specifications and tier prices |

### Query options

List endpoints accept OData-style query parameters:

| Parameter | Example |
|---|---|
| `$filter` | `Name.Contains("shirt") and Published == true` |
| `$orderby` | `Name, Sku desc` |
| `$select` | `Id, Name, Price` |
| `$skip`, `$top` | `$skip=100&$top=50` |

Filters use C#-like expression syntax (`==`, `and`, `or`, string methods such as `Contains`, `StartsWith`, `EndsWith`) and may name only fields of the returned model, up to 512 characters. At most 100 items are returned per call, whatever `$top` says, so page with `$skip`. An invalid option returns 400 with an error message.

### Frontend API

The Frontend API is not a separate set of URLs: storefront controllers (catalog, product, shopping cart, checkout, orders, account, blog, news, pages and others) are marked as API endpoints and accept a Frontend API token instead of the storefront cookie.

1. Get a token: `POST /TokenWeb/Guest` for an anonymous visitor, or `POST /TokenWeb/Login` with `{ "email": "...", "password": "<Base64>" }` for a registered customer. Both return an access token and a refresh token.
2. Refresh an expired access token with `POST /TokenWeb/Refresh`, sending both tokens.
3. For state-changing requests get an antiforgery token from `GET /TokenWeb/Antiforgery` and send it in the `X-CSRF-TOKEN` header.
4. Request bodies are form data by default; set `FrontendAPI:JsonContentType` to `true` to send JSON.

The complete list of operations is in the v2 document at `/scalar/v2` on a development instance.

### Tips and common mistakes

- 401 on every call after enabling: the module flag is still off, or the token was issued before you changed the secret key.
- "User not exists or password is wrong": the password was not Base64-encoded, or the API user is inactive.
- 403 on a specific controller: the customer behind the API user lacks that permission.
- Code of the module: [src/Modules/Grand.Module.Api](https://github.com/grandnode/grandnode2/tree/develop/src/Modules/Grand.Module.Api).

### Related

- [Architecture overview](https://grandnode.com/developers-architecture-overview)
- Permissions and customer groups: [Customers](https://grandnode.com/docs-customers)

- [Web page](https://grandnode.com/developers-rest-api)
