> For the complete documentation index, see [llms.txt](https://docs.live-eo.com/tradeaware/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/manage-products-and-plot-links.md).

# Manage products and plot links

Product-to-plot linking lets you work with EUDR data at the level you trade: products. Instead of selecting plots for every transaction, you:

1. create the products you buy,
2. share each product with the suppliers who deliver it,
3. let each supplier link the plots that product comes from, once, in the web app or through the API, with an optional valid-until date,
4. reference the product in your transactions, so TradeAware uses the linked plots for the Due Diligence Statement (DDS).

This guide covers your side as the buyer: products, plot data requests and transactions. Your suppliers link their plots in the web app (see For suppliers: link plots to your buyers' products) or through the API with `PUT /products/{productId}/plots`.

Important: before you start

* Product-to-plot linking must be available in the environment you call. If it isn't, every product endpoint returns 403 with the message "Product-to-plot connections are disabled in this environment."
* You need API access and an access token. API access is available to Enterprise customers, see [API Access and Business Account Registration](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/api-access-and-business-account-registration) and [Authentication](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/authentication). The product endpoints accept the same machine-to-machine tokens as the transaction endpoints.
* Using products in transactions needs Transaction Management. See [Manage your transactions](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/manage-your-transactions).
* Buyer and supplier must be connected before a product can be shared. See [Connect with your supply chain](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/connect-with-your-supply-chain).
* Your suppliers need their plots in TradeAware before they can link them. They can upload them in the web app, see [Upload and Analyzing Plots](https://docs.live-eo.com/tradeaware/using-the-tradeaware-web-app/upload-and-analyzing-plots).

All examples use the production base URL <https://api.tradeaware.live-eo.com>. Replace \<ACCESS\_TOKEN> with your token.

### Key concepts

<table data-header-hidden><thead><tr><th width="200.935546875"></th><th></th><th></th></tr></thead><tbody><tr><td>Concept</td><td>What it is</td><td>Who writes it</td></tr><tr><td>Product</td><td>An item the buyer buys: name, EUDR HS code, optional reference and description. The commodity is derived from the HS code.</td><td>The buyer (owner)</td></tr><tr><td>Product supplier</td><td>A supplier the product is shared with. Sharing is how a buyer requests plot data from a supplier.</td><td>The buyer</td></tr><tr><td>Plot link</td><td>The set of plots a supplier links to the product, with one optional valid-until date (plotsExpiryDate) for the whole set. Each supplier of a product has its own plot link.</td><td>The supplier, in the web app or with PUT /products/{productId}/plots</td></tr><tr><td>Product-linked component</td><td>A transaction component that references products. At DDS submission, TradeAware adds the linked plots.</td><td>The buyer</td></tr></tbody></table>

### Products and plot data requests

#### Step 1: Create your products

Use `POST /products/batch` to create up to 1,000 products per request, for example when you load your catalog or sync it from your ERP. Use POST /products for a single product. Both take the same fields.

| Field           | Required | Description                                                                                                                              |
| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| name            | Yes      | Name of the product, up to 255 characters.                                                                                               |
| hsCode          | Yes      | An HS code covered by the EUDR. Separators are optional: "4403.12" and "440312" are the same. Codes outside the EUDR scope are rejected. |
| buyerProductRef | No       | Your own reference, for example your ERP material number, up to 255 characters. The batch endpoint uses it to detect duplicates.         |
| description     | No       | Short description, up to 1,000 characters.                                                                                               |

The commodity is derived from hsCode and can't be set. Fields the API doesn't know are ignored.

Create products in batch

```
curl -X POST https://api.tradeaware.live-eo.com/products/batch \
  --header 'Authorization: Bearer <ACCESS_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "products": [
      { "name": "Cocoa beans, Grade A", "hsCode": "1801", "buyerProductRef": "SKU-10442" },
      { "name": "Oak boards 25mm", "hsCode": "4407.91", "buyerProductRef": "SKU-20871" },
      { "name": "Cotton T-shirt", "hsCode": "6109", "buyerProductRef": "SKU-30915" }
    ]
  }'
```

The endpoint always responds with 201, also when every row was rejected. Read status and the per-row results, not the HTTP code. Example response:

```
{
  "status": "PARTIAL_FAILURE",
  "summary": { "total": 3, "created": 1, "failed": 2 },
  "created": [
    {
      "index": 0,
      "product": {
        "id": "5f0c2a4e-8b1d-4c7a-9e3f-2d6b8a1c4e70",
        "name": "Cocoa beans, Grade A",
        "hsCode": "1801",
        "description": null,
        "commodity": "cocoa",
        "buyerProductRef": "SKU-10442",
        "ownerId": "02c36247-1aad-41ba-aa49-f114ca4c263f",
        "ownerName": "Acme Trading",
        "ownedByCaller": true,
        "createdAt": "2026-10-06T09:12:03.000Z",
        "updatedAt": "2026-10-06T09:12:03.000Z"
      }
    }
  ],
  "failed": [
    {
      "index": 1,
      "name": "Oak boards 25mm",
      "code": "PRODUCT_ALREADY_EXISTS",
      "message": "A product with this buyerProductRef already exists.",
      "existingProductId": "0f8e6a3c-1d2b-4e5f-8a9b-0c1d2e3f4a5b"
    },
    {
      "index": 2,
      "name": "Cotton T-shirt",
      "code": "VALIDATION_FAILED",
      "message": "hsCode should be listed in Annex I to Regulation (EEC) No 2658/87"
    }
  ]
}
```

status is SUCCESS, PARTIAL\_FAILURE or FAILURE (no product created). index is the row's zero-based position in your products array.

<table data-header-hidden><thead><tr><th width="265.9580078125"></th><th></th></tr></thead><tbody><tr><td>code</td><td>Meaning</td></tr><tr><td>VALIDATION_FAILED</td><td>The row breaks one of the field rules above, for example an HS code outside the EUDR scope. message names the rule.</td></tr><tr><td>PRODUCT_ALREADY_EXISTS</td><td>Your business already has a product with this buyerProductRef. existingProductId is its id. Nothing was created.</td></tr><tr><td>DUPLICATE_IN_REQUEST</td><td>An earlier row in the same request has this buyerProductRef. duplicateOfIndex points to that row. This row isn't created.</td></tr><tr><td>INTERNAL_ERROR</td><td>The row couldn't be saved. Send it again.</td></tr></tbody></table>

Retries are safe for rows with a buyerProductRef. Each reference is checked against all your existing products, including products created with POST /products or in the web app. If a request times out, send the same rows again: rows that were created the first time come back as PRODUCT\_ALREADY\_EXISTS with their id. Rows without a buyerProductRef are never treated as duplicates and are created again.

Note: Send batch requests one after another, not in parallel. Two requests running at the same time with the same buyerProductRef can both create the product.

Create a single product

```
curl -X POST https://api.tradeaware.live-eo.com/products \
  --header 'Authorization: Bearer <ACCESS_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Cocoa beans, Grade A",
    "hsCode": "1801",
    "buyerProductRef": "SKU-10442",
    "description": "Raw cocoa beans"
  }'
```

The response is 201 with the product, in the same shape as product above. Store the returned id against your own reference: every other call uses it.

Important: `POST /products` doesn't check for duplicates. Sending the same product twice creates it twice. If a call fails or times out, check GET /products for your buyerProductRef before you send it again.

#### Step 2: Find your suppliers' business ids

Products are shared with suppliers by their TradeAware business id. List your connected suppliers:

```
curl https://api.tradeaware.live-eo.com/business-connections/suppliers \
  --header 'Authorization: Bearer <ACCESS_TOKEN>'
```

Each connection's supplier is in targetBusiness: use targetBusiness.id. A supplier only appears here once they have accepted your invitation.

#### Step 3: Share the product with its suppliers

Sharing a product with a supplier is how you request plot data from them.

Endpoint: `PUT /products/{productId}/suppliers`

The body is the complete supplier list for the product. Suppliers in the list are added, suppliers missing from it are removed, and sending the same list again changes nothing.

```
curl -X PUT https://api.tradeaware.live-eo.com/products/5f0c2a4e-8b1d-4c7a-9e3f-2d6b8a1c4e70/suppliers \
  --header 'Authorization: Bearer <ACCESS_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "supplierIds": [
      "c1dbc78d-a127-454a-961b-ef30becf37fd",
      "a8593d6b-1d23-47d0-9396-72d1a4862d19"
    ]
  }'
```

Example response (200), the resulting supplier list:

```
[
  {
    "supplierId": "c1dbc78d-a127-454a-961b-ef30becf37fd",
    "supplierName": "Abidjan Cocoa Cooperative",
    "plotsExpiryDate": null,
    "assignedPlotsCount": 0
  },
  {
    "supplierId": "a8593d6b-1d23-47d0-9396-72d1a4862d19",
    "supplierName": "San Pedro Growers",
    "plotsExpiryDate": null,
    "assignedPlotsCount": 0
  }
]
```

* The request is all-or-nothing. If any id isn't a supplier connected to you, you get 400 "No business connection to one or more of the supplier ids sent" and nothing changes. The response doesn't say which id was rejected.
* Up to 1,000 suppliers per product. Each id may appear once.
* Send {"supplierIds": \[]} to stop sharing the product with everyone.
* Each newly added supplier gets an in-app notification in TradeAware: "\[Your business] is requesting plots for \[Product]", and then links its plots in the web app or through the API. Sending an unchanged list doesn't notify again. There is no email or webhook.
* Each supplier only sees its own entry for the product, never your other suppliers.

Important: Removing a supplier also removes the plots they linked to the product. If you add them back later, they start with no linked plots. Always send the full list, not only the suppliers you want to add.

#### Step 4: Track readiness across your products

Products have no status field. You derive it per supplier from assignedPlotsCount and plotsExpiryDate.

Get all your products with a supplier preview in one call:

```
curl "https://api.tradeaware.live-eo.com/products?includes=suppliers" \
  --header 'Authorization: Bearer <ACCESS_TOKEN>'
```

Each product then carries suppliers (a preview of at most 3, alphabetical) and suppliersTotal. When suppliersTotal is larger than the preview, get the full list for that product with `GET /products/{productId}/suppliers`.

| Status     | Rule                                                                           |
| ---------- | ------------------------------------------------------------------------------ |
| Not shared | The product has no suppliers (suppliersTotal is 0).                            |
| Shared     | The supplier is listed and assignedPlotsCount is 0.                            |
| Ready      | assignedPlotsCount is above 0, and plotsExpiryDate is empty or today or later. |
| Expired    | plotsExpiryDate is before today.                                               |

For the product as a whole, take the supplier furthest behind: Expired if any supplier is Expired, otherwise Shared if any supplier is Shared, otherwise Ready. These are the statuses the web app shows on the Products page.

Note: `GET /products` isn't paginated. It returns all your products, newest first, together with products other businesses shared with you (ownedByCaller: false). Filter on ownedByCaller: true for your own.

#### Step 5: See which plots a supplier linked

Endpoint: `GET /products/{productId}/plots`

Returns one entry per supplier, each with its linked plots and valid-until date. A supplier that hasn't linked any plots yet is still listed, with an empty assignedPlots.

```
curl https://api.tradeaware.live-eo.com/products/5f0c2a4e-8b1d-4c7a-9e3f-2d6b8a1c4e70/plots \
  --header 'Authorization: Bearer <ACCESS_TOKEN>'
```

Example response (200), plot objects shortened. They use the same shape as GET /plots.

```
[
  {
    "supplierId": "c1dbc78d-a127-454a-961b-ef30becf37fd",
    "supplierName": "Abidjan Cocoa Cooperative",
    "plotsExpiryDate": "2027-03-31",
    "assignedPlots": [
      {
        "id": "a78c1a68-d326-4217-a131-893f3c47a064",
        "name": "Farm A - North block",
        "commodities": ["cocoa"],
        "countryCode": "CI",
        "surfaceArea": 38000
      }
    ]
  },
  {
    "supplierId": "a8593d6b-1d23-47d0-9396-72d1a4862d19",
    "supplierName": "San Pedro Growers",
    "plotsExpiryDate": null,
    "assignedPlots": []
  }
]
```

Plots a supplier has deleted no longer appear here or in assignedPlotsCount.

#### Change or delete a product

* Change: `PATCH /products/{id}` with any of name, hsCode, buyerProductRef and description. Only the fields you send change. The owner can't be changed.
* Changing hsCode doesn't change the product's suppliers or their linked plots. TradeAware doesn't check the linked plots against the new commodity.
* Delete: `DELETE /products/{id}` returns 204. It also removes the product's supplier list and the plots every supplier linked to it. A deleted product no longer appears in GET /products. Transactions that aren't submitted yet and still reference the product no longer get its linked plots.

### For transactions: use products instead of selecting plots

On a transaction component, productIds references products you own. When the transaction is submitted, TradeAware adds the plots that the component's suppliers have linked to those products, so you don't select plots for every transaction.

**How plots are chosen at DDS submission**

In this table, suppliers means supplierBusinessIds, or supplierBusinessId for a single supplier.

| The component has                 | Plots in the DDS                                                                                                                               |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| productIds and suppliers          | The plots each listed supplier has linked to the listed products at the moment of submission.                                                  |
| productIds, suppliers and plotIds | The linked plots plus the plotIds, each plot once. With supplierBusinessIds, plotIds must include at least one plot from each listed supplier. |
| plotIds without productIds        | Exactly those plots.                                                                                                                           |
| Suppliers only                    | All plots of the listed suppliers, as described in Manage your transactions.                                                                   |

Rules for product-linked components

* List the suppliers on the component. Linked plots are only added for the suppliers listed on it, so a component with productIds but no suppliers gets no linked plots.
* productIds must be products you own. Otherwise you get 400 "No product found with the provided ID(s). (Product ID(s): …)".
* Omit plotIds unless you want to add plots on top of the linked ones. If you send plotIds with supplierBusinessIds, include at least one plot from every listed supplier, even suppliers that already linked plots to the product. Otherwise you get 400 "Some suppliers were referenced but do not have any plots associated with them". The plots must belong to your suppliers. An empty plotIds array is rejected.
* Linked plots are read at submission. If a supplier changes their linked plots before you submit, the DDS uses the updated set.
* Check every supplier's linked plots before you submit. A listed supplier that hasn't linked plots to the product adds no plots, and the API returns no error. Call `GET /products/{productId}/plots` and check that each supplier on the component has plots in assignedPlots.
* Expired links are still used. Plots a supplier deleted are left out, but a link whose valid-until date (plotsExpiryDate) has passed is still used. Check the date before you submit.

Example: an import with one product-linked component.

```
curl -X POST https://api.tradeaware.live-eo.com/transactions \
  --header 'Authorization: Bearer <ACCESS_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "customId": "PO-2026-10-0042",
    "activityType": "IMPORT",
    "countryOfActivity": "DE",
    "components": [
      {
        "hsn": "1801",
        "description": "Raw cocoa beans, Grade A",
        "quantity": 24000,
        "unit": "KGM",
        "productIds": ["5f0c2a4e-8b1d-4c7a-9e3f-2d6b8a1c4e70"],
        "supplierBusinessIds": ["c1dbc78d-a127-454a-961b-ef30becf37fd"],
        "dateOfProduction": "2026-06-01T00:00:00.000Z",
        "endDateOfProduction": "2026-08-31T00:00:00.000Z"
      }
    ]
  }'
```

productIds also works on POST /transactions/{id}/components and PATCH /components/{id}. On PATCH, the list you send replaces the component's products. The component's plotIds only lists the plots you sent yourself, not the linked plots: those are added at submission.

When you read a component back, its products array lists the linked products (id, name, hsCode):

* `GET /transactions/{id}` and `GET /components/{id}` include it by default.
* `GET /transactions` includes it only with includes=components.products.
* A component without products has no products key at all, not an empty array.

For everything else about transactions, such as activity types, TRACES environments and submission, see [Manage your transactions](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/manage-your-transactions).

### Errors and troubleshooting

<table data-header-hidden><thead><tr><th width="87.3857421875"></th><th></th><th></th></tr></thead><tbody><tr><td>Code</td><td>Message</td><td>Cause</td></tr><tr><td>400</td><td>Field rule messages, for example "hsCode should be listed in Annex I to Regulation (EEC) No 2658/87", "You can share a product with up to 1000 suppliers." or "You can create up to 1000 products per request. Split the list into several requests."</td><td>The body breaks a field rule. Supplier ids must be unique UUIDs, and lists are limited to 1,000 entries.</td></tr><tr><td>400</td><td>"No business connection to one or more of the supplier ids sent"</td><td>PUT …/suppliers: an id isn't a supplier connected to you. Nothing was changed.</td></tr><tr><td>400</td><td>"No product found with the provided ID(s). (Product ID(s): …)"</td><td>A component references a product you don't own.</td></tr><tr><td>401</td><td><br></td><td>The access token is missing or expired. Tokens are valid for 24 hours.</td></tr><tr><td>403</td><td>"Product-to-plot connections are disabled in this environment."</td><td>Product-to-plot linking isn't available in the environment you called.</td></tr><tr><td>403</td><td>"Only the product owner can change its suppliers"</td><td>PUT /products/{productId}/suppliers on a product another business shared with you. You can read it, but only its owner can change its suppliers.</td></tr><tr><td>403</td><td>"Only a supplier of the product can assign plots to it"</td><td>PUT /products/{productId}/plots on your own product. Only the suppliers you shared it with can link plots to it.</td></tr><tr><td>404</td><td>"Product not found"</td><td>You have no access to the product: it doesn't exist, was deleted, or isn't yours and wasn't shared with you. PATCH and DELETE /products/{id} also return 404 for a product shared with you.</td></tr></tbody></table>

"***My supplier linked plots, but the transaction has none.***" Check that the supplier is listed on the component, that the product id is the one they linked plots to, and that GET /products/{productId}/plots shows their plots.

"***A supplier disappeared from my product and their plots are gone.***" `PUT /products/{productId}/suppliers` was sent without that supplier. Add them again and ask them to link their plots again.

### Limits and behavior

* Up to 1,000 products per batch request and 1,000 suppliers per product. A supplier can link up to 1,000 plots to one product.
* `GET /products` isn't paginated. includes=suppliers returns a preview of up to 3 suppliers per product plus suppliersTotal. To get the full list of suppliers, use GET /products/{productId}/suppliers.
* Sharing is one PUT per product.
* `PUT /products/{productId}/suppliers` replaces the whole list, can be repeated safely and is all-or-nothing.
* The maximum request size is 50 MB, see [API Access and Business Account Registration](https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/api-access-and-business-account-registration).
* Each workspace is a separate business with its own API client. Products, suppliers and linked plots belong to one workspace and aren't visible from another.

### Not available yet

<table data-header-hidden><thead><tr><th width="238.775390625"></th><th></th></tr></thead><tbody><tr><td>What's missing</td><td>What this means</td></tr><tr><td>Product status field</td><td>Products don't carry a status. The API returns the counts and dates the status is based on.</td></tr><tr><td>Reading a single product</td><td>There's no GET /products/{id}. A product can only be read through the full list.</td></tr><tr><td>Pagination</td><td>GET /products returns all products in one response.</td></tr><tr><td>Webhooks</td><td>You get no webhook event when a supplier links plots. Notifications are in the web app only.</td></tr><tr><td>Export</td><td>There's no export of products, suppliers or linked plots.</td></tr><tr><td>Valid-until enforcement</td><td>A link stays active after its valid-until date has passed and is still used in your transactions.</td></tr><tr><td>Risk level on linked plots</td><td>GET /products/{productId}/plots doesn't return the plots' risk level.</td></tr><tr><td>Plots further down the supply chain</td><td>A supplier can only link plots its own business owns. Plots that belong to your supplier's suppliers can't be linked to your product.</td></tr><tr><td>GeoJSON on a transaction</td><td>A transaction can't take plots as a GeoJSON file. It only takes ids of plots already in TradeAware.</td></tr><tr><td>Naming the rejected id</td><td>The 400 response of PUT /products/{productId}/suppliers doesn't say which id was rejected.</td></tr></tbody></table>

### Endpoint summary

<table data-header-hidden><thead><tr><th width="125.65234375"></th><th></th><th></th></tr></thead><tbody><tr><td>Method</td><td>Endpoint</td><td>Purpose</td></tr><tr><td>POST</td><td>/products/batch</td><td>Create up to 1,000 products, with a result per row</td></tr><tr><td>POST</td><td>/products</td><td>Create one product</td></tr><tr><td>GET</td><td>/products</td><td>List your products (includes=suppliers optional)</td></tr><tr><td>PATCH</td><td>/products/{id}</td><td>Change a product</td></tr><tr><td>DELETE</td><td>/products/{id}</td><td>Delete a product with its suppliers and linked plots</td></tr><tr><td>GET</td><td>/products/{productId}/suppliers</td><td>List a product's suppliers with linked-plot counts and valid-until dates</td></tr><tr><td>PUT</td><td>/products/{productId}/suppliers</td><td>Set a product's suppliers</td></tr><tr><td>GET</td><td>/products/{productId}/plots</td><td>List the linked plots per supplier</td></tr><tr><td>PUT</td><td>/products/{productId}/plots</td><td>Suppliers only: set your linked plots for a product shared with you</td></tr></tbody></table>

Full request and response schemas are in the [API reference](https://api.tradeaware.live-eo.com/api).

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.live-eo.com/tradeaware/using-the-tradeaware-api/manage-products-and-plot-links.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
