# API Reference

Welcome! This guide will get you up and running with our API. Our goal is to provide a powerful and intuitive interface that is easy to integrate with.

Our API is designed using an **RPC (Remote Procedure Call) style over HTTP**. This means that instead of focusing on RESTful resources (nouns), you'll interact with **operation-centric** methods that represent clear business actions (verbs).

# Core Principles

We designed the API with three key principles in mind to make your development experience as smooth as possible.

- **Domain-driven Design:** Our API is organized into domain-specific **namespaces** that logically group related operations. This domain-oriented design simplifies method discovery and ensures that you are always interacting with the correct part of the system.

- **Business Process-Oriented:** Our API methods are designed to mirror your real-world business processes. Instead of generic data manipulation, you'll call functions that represent distinct steps in your workflow. For example, you'll call `setGreenLotWeight` or `transferGreenLotToLocation`, directly matching the actions your team performs.

- **Data Consistency:** To guarantee data integrity, our API treats each business operation as a single, atomic request. This approach bundles all data changes into one call, preventing the data corruption and inconsistencies that can occur when an operation is split across multiple requests.


# Request Format

{% table %}
- Criteria {% width="30%" %}
- Description

---

- **HTTP Method**
- Our API exclusively uses the `POST` HTTP method for all operations. The specific action you wish to perform (e.g., retrieving, creating, or updating data) should be specified within the request body, rather than by using different HTTP verbs like `GET`, `PUT`, or `DELETE`.

---

- **Protocol & Security**
- All communication with the API must be made over **HTTPS**. For security, connections made over unencrypted HTTP will be rejected.


---

- **Base URL**
- The **base path** for all endpoints is `https://api.cropster.com/rpc/v1` where `v1` is the current version of the API.

---

- **URL Path Schema**
- The endpoints are **hierarchical structure** using dotted namespaces to reflect the business domain. This structure allows for clear organization and easy discovery of endpoints related to specific business operations. The following endpoint naming schema is used for all endpoints: `{domain}.{subdomain}.{operation}` (e.g., `inventory.roast.listRoastedLots`).

---


- **Content-Type**
- All POST requests must include a `Content-Type` header set to `application/json`. Requests with a different content type, or without this header, will be rejected.
  The only exception to this is the OAuth token request, which requires a `Content-Type` of `application/x-www-form-urlencoded`.

---

- **Payload**
- Request and response bodies are in **JSON** format with **camelCase** field names. **Singular nouns** are generally used for fields representing single entities (e.g. `locationId`). **Plural nouns** are used for fields representing lists of entities (e.g. `greenLots`).

{% /table %}

# Authentication

The Cropster API uses the **OAuth 2.0 Client Credentials Grant** flow to authenticate all requests. This is a server-to-server authentication method where applications authenticate directly rather than on behalf of a user.

The process involves two main steps which are explained in the following.

### Generate API Credentials

Before you can connect to the API, you must create a Service Account in the Cropster user interface to obtain a `Client ID` and `Client Secret`.

1.  Log in to your [Cropster account](https://c-sar.cropster.com) and navigate to the **Settings > Service Accounts** section.
2.  Create a new **Service Account**.
3.  Assign a name and the required **OAuth scopes** that define the specific permissions for this account (e.g., `inventory.roast` to access roast inventory data).
4.  Upon creation, your `Client ID` and `Client Secret` will be displayed.
    
{% admonition type="warning" name="Secure Your Credentials" %}
  Your `Client Secret` is highly sensitive and is only displayed once upon creation. Store it securely and treat it like a password. Do not expose it in client-side code.
{% /admonition %}



## Request Access Token

Next, exchange your credentials for a temporary access token by making a `POST` request against the OAuth2 token endpoint: `https://auth.cropster.com/oauth2/token`.

Note that the `scope` parameter must include all OAuth scopes that are required for subsequent API requests. For example, to access roasted inventory data, the `inventory.roast` scope
must be included.

{% openapi-code-sample
  descriptionFile="../oauth/oauth-api.yaml"
  operationId="oauth2Token"
/%}

If the request is successful, the response contains the access token that must be set as
`Bearer: <ACCESS_TOKEN>` in the `Authorization` header of all subsequent API requests.

{% markdoc-example renderDemo=false %}
```markdown {% title="Successful Response" %}
{
    "access_token": "<ACCESS_TOKEN>",
    "expires_in": 28799,
    "scope": "inventory.green%20inventory.roast%20inventory.blend",
    "token_type": "bearer"
}
```
{% /markdoc-example %}

{% admonition type="info" name="Access Token Lifetime" %}
The access token is valid for **8 hours**. You should cache the token in your application and reuse it until it expires to reduce latency and avoid unnecessary token requests.
{% /admonition %}

---

<h1>Request Format</h1>

This section details the required request format for obtaining an access token and the structure of the response you will receive.

<h2>Request Parameters</h2>

The following parameters are required for the access token request:

{% json-schema
schema={
"$ref": "../oauth/oauth-api.yaml#/components/schemas/oauth2TokenRequest"
}
/%}

<h2>Response Fields</h2>

A successful request returns the access token along with its associated metadata:

{% json-schema
schema={
"$ref": "../oauth/oauth-api.yaml#/components/schemas/oauth2TokenResponse"
}
/%}



# Error Handling

Each API request is returning a status code that indicates if the request was successfully
completed. In the following, the most common HTTP status codes are listed:

{% table %}
- HTTP Status Code {% width="30%" %}
- Description
 
---

- **200 OK**
- The request was successful.

---

- **400 Bad Request**
- The request couldn't be processed by the server due to an invalid request. Possible reasons can be missing or invalid query parameters, invalid JSON payload or malformed URL.
    
---

- **401 Unauthorized**
- The provided authentication credentials are not correct. Please check the section Authentication on how to provide valid authentication credentials.

---

- **403 Forbidden**
- The client doesn't have permissions to perform the request. Please check the endpoint documentation on which permissions are required.
    
---

- **405 Method Not Allowed**
- This error indicates that the requested operation is not supported. For example, this error can be caused when a POST request is not supported by an endpoint.
    
---

- **422 Unprocessable Entity**
- The request failed due to semantic errors in the request body. Examples of semantic errors are missing attributes or the violation of business rules (e.g. creating a green lot with a negative weight).
    
---

- **429 Too Many Requests**
- The client has sent too many requests in a given amount of time. Please check the Rate Limiting section for more details.

---

- **500 Server Error**
- An internal error encountered within Cropster. Please check the Cropster status page to verify if all systems are operational.
    
---
{% /table %}

To handle errors programmatically, each error response contains one of the following error codes:

{% table %}

- Error Code {% width="30%" %}
- Description

---

- **invalid_authentication**
- Indicates that the authentication credentials provided are invalid. For example, the JWT is missing or is expired.

---

- **access_denied**
- Indicates that the user is authenticated but does not have the required permissions to access the requested resource. For example, a user without the appropriate role
  attempts to perform a restricted operation.


---

- **resource_not_found**
- Indicates that the specified resource could not be found. For example, an object is request by an ID that does not exist.

---

- **invalid_request**
- Indicates that the request could not be completed due to invalid or missing information. Please review the documentation and try again.

---

- **invalid_weight**
- Indicates that the weight parameter in the request is invalid. For example, a negative weight amount is specified.

---

- **invalid_price**
- Indicates that the price parameter in the request is invalid. For example, a negative price amount is provided.

---

- **invalid_json**
- Indicates that the provided request is malformed.

---

- **invalid_parameter**
- A general-purpose error indicating that one or more parameters in the request failed validation. This is a fallback error and should include a descriptive message with more detail. Note, this error should not normally be returned unless a more specific code is not applicable.

--- 

- **missing_parameter**
- Indicates that a required parameter is missing from the request. For example, the name property is omitted in the `importGreenLot` request.

---

- **resource_conflict**
- Indicates that the request contains conflicting parameters. For example, the `importGreenLot` request specifies location ID and location at the same time.

---

- **unsupported_currency**
- Indicates that the specified currency is not an ISO 8601 compliant currency
  code.

---

- **unsupported_unit**
- Indicates that the specified weight unit is not supported. Only `KG` and `LBS` are
  currently accepted.

---

- **too_many_requests**
- Indicates that the client has sent too many requests in a given amount of time.

---

- **bulk_operation_failed**
- Indicates that one or more issues occurred during the processing of a bulk request.

---

- **server_error**
- Indicates an internal server error occurred while processing the request. The accompanying error message should provide additional context.

---

{% /table %}

# Pagination

The RPC API uses cursor-based pagination for read-centric, listing endpoints. The results are delivered in reverse-chronological order. This means that the most recently created results appear first in the result set. For the time being, only forward navigation is supported. The default page size is set to 50 items per page. 

The `meta.pagination.endCursor` field in the response indicates if there are more pages. Pass this cursor value in the `params.pagination.after` field of the next request to retrieve the next page. If there are no further pages, `meta.pagination.endCursor` will be null.

---

<h2>Pagination Request Parameters</h2>

{% json-schema
  schema={
    "title": "",
    "type": "object",
    "properties": {
      "params.pagination.after": {
        "type": "string",
        "description": "Cursor value to retrieve the result of the next page. This value is obtained from the `meta.pagination.endCursor` field of the previous response. If not specified or `null`, the first page of results is returned.",
      },
      "params.pagination.limit": {
        "type": "int",
        "description": "Specifies the page size of the result set. The value must be in the range between 1 and 100. If not specified, the default value of 50 is used.",
        "minimum": 1,
        "maximum": 100
      }
    }
  }
/%}

<h2>Pagination Response Field</h2>

{% json-schema
  schema={
    "title": "",
    "type": "object",
    "properties": {
      "meta.pagination.endCursor": {
        "type": "string",
        "description": "Cursor value to retrieve the records of the next page. This value should be passed in the `params.pagination.after` field of the next request. If there are no further pages, this value is `null`."
      }
    }
  }
/%}

<h2>Example</h2>

In the following the pagination mechanism is explained using the `inventory.roast.listRoastedLots` endpoint as an example. The first request retrieves the first page of roasted lots created after `2025-01-01T00:00:00Z` with a page size of 10 records. Notice, the `after` field is set to `null` to indicate that the first page should be returned.

{% markdoc-example renderDemo=false %}
``` JSON {% title="First request to retrive first page" %}
POST <BASE_PATH>/inventory.roast.listRoastedLots
Content-type: application/json
Authorization: Bearer <TOKEN>
{
    "params": {
        "filter": {
            "roastedDate": {
                "from": "2025-01-01T00:00:00Z"
            }
        },
        "pagination": {
            "after": null,
            "limit": 10 
        }
    }
}
```
{% /markdoc-example %}

The response of the above request contains the `meta.pagination.endCursor` field that can be used to retrieve the next page. This value should be passed in the `params.pagination.after` field of the next request. If there are no further pages, `meta.pagination.endCursor` will be null.

{% markdoc-example renderDemo=false %}
```JSON {% title="Paginated response" %}
 {
  "ok": true,
  "result": {
    "roasts": [
      ...
    ]
  },
  "meta": {
    "pagination": {
      "endCursor": 
        "aBc="
    }
  }
}
```
{% /markdoc-example %}


Since an `endCursor` value of `aBc=` was returned in the previous response, there are more pages 
available. To retrieve the next page, use this value in the `params.pagination.after` field in the next request.

{% markdoc-example renderDemo=false %}
``` JSON {% title="Second request to retrieve the next page" %}
POST <BASE_PATH>/inventory.roast.listRoastedLots
Content-type: application/json
Authorization: Bearer <TOKEN>
{
    "params": {
        "filter": {
            "roastedDate": {
                "from": "2025-01-01T00:00:00Z"
            }
        },
        "pagination": {
            "after": "aBc=
        }
    }
}
```
{% /markdoc-example %}

# Rate Limiting

To ensure fair usage and maintain optimal performance of the API, rate limiting is enforced on all endpoints. If a client exceeds the specified limits, the API will respond with an `429 Too Many Requests` HTTP status code.

Our standard rate limits are set as follows:

- **Rate Limit:** 5 requests per second. This is the average throughput your application is allowed to execute.

- **Burst Limit:** 10 requests per second. Allows for short-term spikes in traffic. For example, your application can send up to 10 requests in a single, as long as the average rate remains within the 5 req/sec sustained limit. 

# Versioning

Our API is versioned to ensure that your integration remains stable and that future updates do not introduce breaking changes unexpectedly. We use URI path versioning, where the version is explicitly included in the request path.

- **Current Version:** The current stable version of our API is `v1`. 

- **Future Versions:** 
When we introduce backward-incompatible changes, we will release a new API version (e.g., `v2`, `v3`). Older versions are supported for a reasonable time, with a clear migration guide provided well in advance of retirement.

# AI Tooling

To accelerate your development workflow, you can integrate the Cropster API with various AI assistants and development tools:

### AI Assistant

* **What it does:** Instantly gather quick insights, generate boilerplate code tailored to your use case, or troubleshoot configuration errors on the fly.
* **Where to find it:** Click the **Ask AI** button in the bottom-right corner of any documentation page, or type your question directly into the main search bar.
* **How to use it:** Ask any question related to the Cropster API. The AI Assistant will provide concise answers, code snippets, and direct documentation references. You can also use the integrated copy button to quickly grab a prompt-optimized context of the API to paste into Claude, ChatGPT, or your preferred AI chatbot.

### MCP (Model Context Protocol) Server

* **What it does:** We now host a dedicated MCP server for developers using next-generation AI tools and IDEs (like Cursor, VS Code, Claude, or ChatGPT). This feature streams live API context, schemas, and definitions directly into your local development environment, significantly reducing integration time.
* **Where to find it:** You can reach the MCP server under the following link: **https://docs.cropster.com/mcp**.
* **How to use it:** Connect the Cropster MCP server URL to your IDE or AI client. For a deep dive into how MCP works and how to configure it in your specific environment, check out the [Model Context Protocol Getting Started Guide](https://modelcontextprotocol.io/docs/getting-started/intro).

### Editor Connections via the Copy Button

* **What it does:** Streamline your local development workflow by piping clean API definitions directly into Cursor, VS Code, or AI agents. The **Copy** button preserves markdown and structural formatting in an LLM-friendly schema.
* **Where to find it:** Look for the **Copy** icon next to the title of any section across the API documentation.
* **How to use it:** Click the icon to copy the section's context to your clipboard, or expand the dropdown to select your preferred AI tool. The data is pre-formatted to consume minimal tokens while maximizing accuracy for AI reasoning.
    * If you select **Connect to Cursor** (or another supported IDE), your development environment will launch automatically and prompt the MCP installation. Once connected, this API context will be permanently available within your workspace.


## Servers

```
https://api.cropster.com/rpc/v1
```

## Security

### oauth2

Type: oauth2

## Download OpenAPI description

[API Reference](https://docs.cropster.com/_bundle/api/rpc/reference.yaml)

## Cupping

The `quality.cupping` namespace contains all operations for managing your
cuppings.
 
With this API, you can:
- Retrieve existing cuppings.
- Retrieve existing cupping results.


### Lists Cuppings

 - [POST /quality.cupping.listCuppings](https://docs.cropster.com/api/rpc/reference/cupping/listcuppings.md): Retrieves a list of cuppings, their cupped coffees and their result summary.

### Lists Cupping Results

 - [POST /quality.cupping.listCuppingResults](https://docs.cropster.com/api/rpc/reference/cupping/listcuppingresults.md): Retrieves a list of cupping results and their items for the specified cupped coffee ids. At most 100 cupped coffee ids can be provided.

### Gets cuppings by ids

 - [POST /quality.cupping.getCuppingsByIds](https://docs.cropster.com/api/rpc/reference/cupping/getcuppingsbyids.md): Retrives a list of cuppings, their cupped coffees and their result summary for the given list of cupping ids. A list of at most 100 cupping ids must be provided.

## Physical

The `quality.physical` namespace contains all operations for managing your
physical analyses.
 
With this API, you can:
- Retrieve existing physical analyses.


### Lists Physical Analyses

 - [POST /quality.physical.listPhysicalAnalyses](https://docs.cropster.com/api/rpc/reference/physical/listphysicalanalyses.md): Retrieves a paginated list of physical analyses, optionally filtered by analysis date range or analysed lot.

## Product

The `mrp.product` namespace contains all operations for managing your
products.
 
With this API, you can:
- Synchronize products and variants to C-SAR.


### Sync Products and Variants

 - [POST /mrp.product.syncProducts](https://docs.cropster.com/api/rpc/reference/product/syncproducts.md): Creates or updates products and their variants based on the externalId of the product.
When the product can be found it will be updated, otherwise a new product will be
created. Newly created products must be reviewed manually in C-SAR. Once a variant was
created it is immutable. Products can be created without variants, but variants cannot
be 'null'. Products and variants will not be deleted with this request.
It is possible to delete products and variants in C-SAR.
Be aware that you can only submit 50 products at a time.

## Order

The `mrp.order` namespace contains all operations for managing your
orders.
 
With this API, you can:
- Create or update orders and their order items.


### Set Orders

 - [POST /mrp.order.setOrders](https://docs.cropster.com/api/rpc/reference/order/syncorders.md): Creates or updates orders based on the externalId of the order. If an order with the
externalId does not exist a new order with status OPEN will be created. This status
changes when the order is processed during the fulfillment process. The order date won't
be modified when updating the order. Orders can only be updated as long as their status
is OPEN.
Be aware that you can only submit 50 orders at once.

## Green

The `inventory.green` namespace contains all operations for managing your
green lots.
 
With this API, you can:
- Import green lots from your ERP system to Cropster.
- Transfer green inventory to another location.
- Make weight adjustments to existing green lots.
- Retrieve the existing green lots from you inventory.


### Transfer Green Lot To Location

 - [POST /inventory.green.transferGreenLotToLocation](https://docs.cropster.com/api/rpc/reference/green/transfergreenlottolocation.md): Transfers weight from a given green lot to another location.
 
When transferring green lot weight the following cases need to be distinguish:
 - When the green lot hasn't yet transferred to the destination location, a new green lot in the destination location will be created.
 - When the green lot has already been transferred to the specified destination location, the green lot weight in the destination location will be increased.
 
Please consider the following business constraints before you execute your request:
  - Consumed lots cannot be transferred
  - Transferred weight may not be negative
  - Transferred weight may be less or equal than the origin green lot weight

### Set Green Lot Weight

 - [POST /inventory.green.setGreenLotWeight](https://docs.cropster.com/api/rpc/reference/green/setgreenlotweight.md): Sets a new weight for a given green lot. When the green lot weight set it zero, the
green lot will be marked as consumed. Notice that a negative weight is not supported.

### List Green Lots

 - [POST /inventory.green.listGreenLots](https://docs.cropster.com/api/rpc/reference/green/listgreenlots.md): Retrieves a list of green lots from the inventory.

### Import Green Lot

 - [POST /inventory.green.importGreenLot](https://docs.cropster.com/api/rpc/reference/green/importgreenlot.md): Imports a new green lot from an external system (e.g. ERP) into Cropster by an external
identifier (= externalId). Optionally, providing the warehouse via location
is possible, by setting the location.externalId property.

### Get Green Lots by IDs

 - [POST /inventory.green.getGreenLotsByIds](https://docs.cropster.com/api/rpc/reference/green/getgreenlotsbyids.md): Retrieves green lots based on the provided list of IDs.

### Get Green Lot Details

 - [POST /inventory.green.getGreenLotDetailsByIds](https://docs.cropster.com/api/rpc/reference/green/getgreenlotdetailsbyids.md): Gets detailed information about green lots by their IDs.

## Roast

Currently, the `inventory.roast` namespace allows you to retrieve
roasted lots from your inventory.


### List Roasted Lots

 - [POST /inventory.roast.listRoastedLots](https://docs.cropster.com/api/rpc/reference/roast/getroastedlots.md): Retrieves a list of roasted lots from inventory, optionally filterable by an inclusive
roasting date.

### List Profiles

 - [POST /inventory.roast.listProfiles](https://docs.cropster.com/api/rpc/reference/roast/listprofiles.md): Retrieves a list of processing profiles used for roasting. Can be filtered by created date.

### Get Roasted Lots by IDs

 - [POST /inventory.roast.getRoastedLotsByIds](https://docs.cropster.com/api/rpc/reference/roast/getroastedlotsbyids.md): Retrieves roasted lots based on the provided list of IDs.

### Get Roast Details

 - [POST /inventory.roast.getRoastDetailsByIds](https://docs.cropster.com/api/rpc/reference/roast/getroastdetailsbyids.md): Gets detailed information about roasts by their IDs.

### Get Curves For a Roast

 - [POST /inventory.roast.getRoastCurvesById](https://docs.cropster.com/api/rpc/reference/roast/getroastcurvesbyid.md): Gets the curves for a roast by the roasted lot ID.

### Get Profiles by IDs

 - [POST /inventory.roast.getProfilesByIds](https://docs.cropster.com/api/rpc/reference/roast/getprofilesbyids.md): Retrieves processing profiles by their IDs. Only filters by profile IDs.

## Blend

Currently, the `inventory.blend` namespace allows you to retrieve blended lots
from your inventory.


### List Blended Lots

 - [POST /inventory.blend.listBlendedLots](https://docs.cropster.com/api/rpc/reference/blend/getblendedlotslist.md): Retrieves a list of post-roast blended lots from inventory, optionally filterable by
an inclusive blended date.

## Location

The `core.location` namespace contains all operations for managing your
locations.
 
With this API, you can:
- Retrieve existing locations.


### List Locations

 - [POST /core.location.listLocations](https://docs.cropster.com/api/rpc/reference/location/getlocationlist.md): Retrieves a list of locations based on provided filters.

## Project

The `core.project` namespace contains all operations for managing your
projects.
 
With this API, you can:
- Retrieve existing projects.


### List Projects

 - [POST /core.project.listProjects](https://docs.cropster.com/api/rpc/reference/project/getprojectlist.md): Retrieves a list of projects.

### Import project

 - [POST /core.project.importProject](https://docs.cropster.com/api/rpc/reference/project/importproject.md): Imports a new project from an external system into Cropster.

