Prewave EUDR - Customers - Products API
Allows you to manage customer products, including creating, updating, linking/unlinking products, and deactivating products.
Allows you to manage customer products, including creating, updating, linking/unlinking products, and deactivating products.
openapi: 3.0.1
info:
title: Public Prewave Actions EUDR - Customers - Products API
description: 'Documentation of the Public Prewave API.
## What''s New
### Q1 2026 — Supplier Management, User Management, Actions and Feed
This quarter introduces major v2 upgrades, expanded administrative capabilities, and the new Actions API.
- **Core Releases:** Deployed Supplier Management API v2 and Feed API v2, alongside the all-new Actions API.
- **Enhanced Functionality:** Added robust identifier management, granular user and role configuration, and endpoints for managing supplier connection contacts.
- ⚠️ **Required Migration:** Legacy v1 endpoints for Suppliers and Sites Upsert have been deprecated. Developers must migrate existing integrations to v2 by **May 31, 2027** (original deadline was December 31, 2026).
📖 **[Read the Q1 2026 changelog](https://docs.prewave.com/en/articles/699847-q1-2026-public-api-updates)**
### Q2 2026 — Supplier Screening and External Scores
We have expanded our v2 documentation to include comprehensive integration guidance for supplier screening and validation workflows and identifier-based external score ingestion.
- **New Capabilities:** Added support for optional post-onboarding screening and validation during the create event.
- **External Scores:** Batch POST for multiple supplier sites, per-site history GET, and event-type discovery GET (`/public/v1/scores/externals` and `/public/v1/scores/externals/event-types`). Documented in OpenAPI when enabled for your organization.
- **Developer Resources:** Published new integration examples and detailed identifier validation rules to streamline your implementation process.
📖 **[Read the Q2 2026 changelog](https://docs.prewave.com/en/articles/699849-q2-2026-public-api-updates)**
### Q3 2026 — Scores Webhooks
To support event-driven architectures and eliminate the need for continuous API polling, we are introducing webhooks for score state changes later this year.
- **Event-Driven Architecture:** Register webhook URLs to receive real-time HTTP payloads whenever a supplier''s score updates, so you can drive immediate mitigation responses without polling the API.
- **Availability:** Comprehensive OpenAPI specifications and payload schemas will be published closer to the release date.
- **Note:** Schemas and behaviors are subject to refinement prior to general availability.
Documentation updates will be provided prior to release.
### Q4 2026 — Feed V2
We are enhancing Feed API v2 with additional capabilities on top of the existing `GET /public/v2/feed` contract (see Q1 changelog and OpenAPI for the current Feed v2 integration).
- **Availability:** Details will be announced before release.
- **Note:** Schemas and behaviors are subject to refinement prior to the official release.
Documentation updates will be provided prior to release.
---
## Authentication
Prewave’s public api uses *API tokens* to authenticate against our RESTful service. We’ll provide you an *API-token* that each
endpoint needs present as a http header.
To pass the token in a request, simply add it as a header-parameter with
* key = X-Auth-Token
* value = api-token
See an example in curl below where the api-token would be 12345678-90ab-cdef-1234-567890abcdef
```
curl --request GET \
--url https://REPLACE_WITH_SERVER/public/v1/target/prewave/3975230/alerts \
--header ''X-Auth-Token: 12345678-90ab-cdef-1234-567890abcdef''
```
---
## Manage API Tokens
Before you can obtain your API token, you''ll need the credentials for your API user. These credentials will be
sent to you as part of the company-onboarding. If you haven''t got your credentials yet, please reach out to
your sales-contact at Prewave or contact us via info@prewave.ai
To generate an API Token, navigate to https://www.prewave.com/management/api and log in with the
credentials of your API user. Then click at the button "Create New" and use your new api-token authentication as a header parameter.
You can create multiple API tokens and also remove existing API tokens on https://www.prewave.com/management/api.
API tokens do not expire, therefore you have to maintain the list of API tokens you are using manually.
---
## Default Rate Limits
We have two types of default rate limits. For increased access, please contact customer success.
| Type | Requests per 10 seconds | Requests per Minute |
|-----------------------------------|-------------------------|---------------------|
| GET requests | 100 | 500 |
| POST, PUT, PATCH, DELETE requests | 20 | 100 |
'
version: '1.0'
servers:
- url: https://api.prewave.com
description: Production Environment
security:
- Token authentication: []
tags:
- name: EUDR - Customers - Products
description: Allows you to manage customer products, including creating, updating, linking/unlinking products, and deactivating products.
paths:
/public/v2/eudr/customers/products/outbound/{productId}:
put:
tags:
- EUDR - Customers - Products
summary: Update an existing outbound product
description: "\nUpdate an existing outbound product's information.\n\nAll product fields in the payload can be updated; changes are persisted immediately.\n\n**Required permission:** `access_public_products` and `UPDATE` on the product\n "
operationId: updateOutboundProduct
parameters:
- name: productId
in: path
description: The ID of the product.
required: true
schema:
type: integer
format: int32
example: 301
requestBody:
description: Updated outbound product payload.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicOutboundProductPayload'
examples:
Update outbound product:
summary: Revised name, reference, and HS code
description: Update outbound product
value: '{"name":"Oak Dining Table","scientificName":"Quercus robur","reference":"OUT-2025-001","hsCode":"940360","annualRevenue":null,"annualQuantity":null,"unit":null}'
required: true
responses:
'200':
description: Product updated successfully.
'400':
description: Errors if related data not found or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
HS Code not found:
description: HS Code not found
value:
code: hs_code_not_found
message: 'HS code not found: HSCode=123456'
solution: Check if the identifier provided is correct or create the record first
Supplier not found:
description: Supplier not found
value:
code: supplier_not_found
message: 'Supplier not found: ID=123456'
solution: Check if the identifier provided is correct or create the record first
Missing product scientific name:
description: Missing product scientific name
value:
code: missing_wooden_product_scientific_name
message: Missing product scientific name for wooden product
solution: Provide a scientific name for wooden products
'403':
description: '403 Forbidden - Authentication or authorization failure. This status code is returned when: (1) the request lacks valid authentication credentials (missing or invalid X-Auth-Token header), or (2) the authenticated user does not have the required permission to access this resource.'
content:
application/json:
schema:
$ref: '#/components/schemas/AccessDeniedErrorDTO'
examples:
Access denied example:
summary: User lacks necessary permissions or authentication
value: "{\n \"loggedIn\": true,\n \"code\": \"access_denied\",\n \"message\": \"Access denied: you don't have necessary permissions to access this resource\",\n \"solution\": \"Contact support for appropriate permissions\"\n }"
'500':
description: 500 Internal Server Error - An unexpected error occurred on the server. The request may or may not have been processed.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Error - Server Error:
summary: Unexpected server error
value: "{\n \"code\": \"internal_error\",\n \"message\": \"An unexpected error occurred\",\n \"solution\": \"Please try again later or contact support\"\n }"
'429':
description: '429 Too Many Requests - API rate limit exceeded. The request has been rejected because the rate limit for this endpoint has been exceeded. Default rate limits: GET requests - 100 per 10 seconds, 500 per minute; POST/PUT/PATCH/DELETE requests - 20 per 10 seconds, 100 per minute. For increased access, please contact customer success.'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiRateLimitResponse'
examples:
Rate limit exceeded example:
summary: API rate limit exceeded
value: "{\n \"error\": \"API rate limit exceeded\",\n \"message\": \"You have reached the maximum allowed requests. Please try again later or upgrade your plan for increased access\",\n \"requestLimit\": 20,\n \"requestCount\": 20,\n \"limits\": [\n {\n \"requestLimit\": 20,\n \"timeInSeconds\": 10\n },\n {\n \"requestLimit\": 100,\n \"timeInSeconds\": 60\n }\n ],\n \"currentTime\": \"2026-01-15T10:30:00\",\n \"nextResetAt\": \"2026-01-15T10:30:10\"\n }"
/public/v2/eudr/customers/products/inbound/{productId}:
put:
tags:
- EUDR - Customers - Products
summary: Update an existing inbound product
description: "\nUpdate an existing inbound product's information.\n\nAll product fields in the payload can be updated; changes are persisted immediately.\n\n**Required permission:** `access_public_products` and `UPDATE` on the product\n "
operationId: updateInboundProduct
parameters:
- name: productId
in: path
description: The ID of the product.
required: true
schema:
type: integer
format: int32
example: 301
requestBody:
description: Updated inbound product payload.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicInboundProductPayload'
examples:
Update inbound product:
summary: Revised name, reference, HS code, and supplier
description: Update inbound product
value: '{"name":"Oak Wood Planks","scientificName":"Quercus robur","reference":"INB-2025-001","hsCode":"440799","annualSpend":null,"annualQuantity":null,"unit":null,"supplierId":102006215}'
required: true
responses:
'200':
description: Product updated successfully.
'400':
description: Errors if related data not found or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
HS Code not found:
description: HS Code not found
value:
code: hs_code_not_found
message: 'HS code not found: HSCode=123456'
solution: Check if the identifier provided is correct or create the record first
Supplier not found:
description: Supplier not found
value:
code: supplier_not_found
message: 'Supplier not found: ID=123456'
solution: Check if the identifier provided is correct or create the record first
Missing product scientific name:
description: Missing product scientific name
value:
code: missing_wooden_product_scientific_name
message: Missing product scientific name for wooden product
solution: Provide a scientific name for wooden products
'403':
description: '403 Forbidden - Authentication or authorization failure. This status code is returned when: (1) the request lacks valid authentication credentials (missing or invalid X-Auth-Token header), or (2) the authenticated user does not have the required permission to access this resource.'
content:
application/json:
schema:
$ref: '#/components/schemas/AccessDeniedErrorDTO'
examples:
Access denied example:
summary: User lacks necessary permissions or authentication
value: "{\n \"loggedIn\": true,\n \"code\": \"access_denied\",\n \"message\": \"Access denied: you don't have necessary permissions to access this resource\",\n \"solution\": \"Contact support for appropriate permissions\"\n }"
'500':
description: 500 Internal Server Error - An unexpected error occurred on the server. The request may or may not have been processed.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Error - Server Error:
summary: Unexpected server error
value: "{\n \"code\": \"internal_error\",\n \"message\": \"An unexpected error occurred\",\n \"solution\": \"Please try again later or contact support\"\n }"
'429':
description: '429 Too Many Requests - API rate limit exceeded. The request has been rejected because the rate limit for this endpoint has been exceeded. Default rate limits: GET requests - 100 per 10 seconds, 500 per minute; POST/PUT/PATCH/DELETE requests - 20 per 10 seconds, 100 per minute. For increased access, please contact customer success.'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiRateLimitResponse'
examples:
Rate limit exceeded example:
summary: API rate limit exceeded
value: "{\n \"error\": \"API rate limit exceeded\",\n \"message\": \"You have reached the maximum allowed requests. Please try again later or upgrade your plan for increased access\",\n \"requestLimit\": 20,\n \"requestCount\": 20,\n \"limits\": [\n {\n \"requestLimit\": 20,\n \"timeInSeconds\": 10\n },\n {\n \"requestLimit\": 100,\n \"timeInSeconds\": 60\n }\n ],\n \"currentTime\": \"2026-01-15T10:30:00\",\n \"nextResetAt\": \"2026-01-15T10:30:10\"\n }"
/public/v2/eudr/customers/products/outbound:
get:
tags:
- EUDR - Customers - Products
summary: Get a list of outbound products
description: "\nRetrieve a paginated list of outbound products for EUDR compliance.\n\nOutbound products are products that your organization sells or distributes.\nEach item includes linked inbound product IDs when BOM relationships exist.\n\n**Use Cases**:\n- List all outbound products\n- Filter products by various criteria\n- Export product data for compliance reporting\n\n**Pagination**:\n- Results are paginated (default: 10 items per page)\n- Use `page` and `size` query parameters to control pagination\n\n**Required permission:** `access_public_products`\n "
operationId: findOutboundProducts
parameters:
- name: ids
in: query
description: Filter by product IDs. When set, other filters are ignored.
required: false
schema:
type: array
description: Filter by product IDs. When set, other filters are ignored.
example:
- 1
- 2
- 3
items:
type: integer
description: Filter by product IDs. When set, other filters are ignored.
format: int32
example:
- 1
- 2
- 3
- name: q
in: query
description: Full text search query for outbound product names, references or linked inbound product names, references or suppliers.
required: false
schema:
type: string
description: Full text search query for outbound product names, references or linked inbound product names, references or suppliers.
example: soy oil
example: soy oil
- name: page
in: query
description: Page number (0-based)
required: false
schema:
type: integer
default: 0
example: 0
- name: size
in: query
description: Page size
required: false
schema:
type: integer
default: 10
example: 10
responses:
'200':
description: Paginated list of outbound products.
content:
application/json:
schema:
type: object
properties:
content:
type: array
items:
$ref: '#/components/schemas/PublicOutboundProduct'
size:
type: integer
format: int32
number:
type: integer
format: int32
totalElements:
type: integer
format: int32
totalPages:
type: integer
format: int32
numberOfElements:
type: integer
format: int32
first:
type: boolean
last:
type: boolean
empty:
type: boolean
examples:
Outbound products page:
summary: Outbound products with linked inbound product IDs
description: Outbound products page
value: '{"content":[{"id":101,"customerId":100,"orgId":200,"name":"Oak Dining Table","scientificName":"Quercus robur","reference":"OUT-2025-001","commodity":{"id":1,"name":"Timber"},"hsCode":{"code":"940360","description":"Wooden furniture of a kind used in the dining room","commodityId":1},"inboundProducts":[201,202]},{"id":102,"customerId":100,"orgId":200,"name":"Plywood Cabinet","scientificName":null,"reference":"OUT-2025-002","commodity":{"id":1,"name":"Timber"},"hsCode":{"code":"441510","description":"Plywood, veneered panels and similar laminated wood","commodityId":1},"inboundProducts":[203]}],"pageable":{"pageNumber":0,"pageSize":10,"sort":{"empty":true,"sorted":false,"unsorted":true},"offset":0,"paged":true,"unpaged":false},"totalPages":1,"totalElements":2,"last":true,"size":10,"number":0,"numberOfElements":2,"sort":{"empty":true,"sorted":false,"unsorted":true},"first":true,"empty":false}'
'400':
description: Invalid pagination or filter parameters.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Invalid page number:
description: Invalid page number
value:
code: invalid_request
message: Page number must be non-negative
solution: Provide a valid page number (0 or greater)
Invalid page size:
description: Invalid page size
value:
code: invalid_request
message: Page size must be between 1 and 100
solution: Provide a valid page size
'403':
description: '403 Forbidden - Authentication or authorization failure. This status code is returned when: (1) the request lacks valid authentication credentials (missing or invalid X-Auth-Token header), or (2) the authenticated user does not have the required permission to access this resource.'
content:
application/json:
schema:
$ref: '#/components/schemas/AccessDeniedErrorDTO'
examples:
Access denied example:
summary: User lacks necessary permissions or authentication
value: "{\n \"loggedIn\": true,\n \"code\": \"access_denied\",\n \"message\": \"Access denied: you don't have necessary permissions to access this resource\",\n \"solution\": \"Contact support for appropriate permissions\"\n }"
'500':
description: 500 Internal Server Error - An unexpected error occurred on the server. The request may or may not have been processed.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Error - Server Error:
summary: Unexpected server error
value: "{\n \"code\": \"internal_error\",\n \"message\": \"An unexpected error occurred\",\n \"solution\": \"Please try again later or contact support\"\n }"
'429':
description: '429 Too Many Requests - API rate limit exceeded. The request has been rejected because the rate limit for this endpoint has been exceeded. Default rate limits: GET requests - 100 per 10 seconds, 500 per minute; POST/PUT/PATCH/DELETE requests - 20 per 10 seconds, 100 per minute. For increased access, please contact customer success.'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiRateLimitResponse'
examples:
Rate limit exceeded example:
summary: API rate limit exceeded
value: "{\n \"error\": \"API rate limit exceeded\",\n \"message\": \"You have reached the maximum allowed requests. Please try again later or upgrade your plan for increased access\",\n \"requestLimit\": 100,\n \"requestCount\": 100,\n \"limits\": [\n {\n \"requestLimit\": 100,\n \"timeInSeconds\": 10\n },\n {\n \"requestLimit\": 500,\n \"timeInSeconds\": 60\n }\n ],\n \"currentTime\": \"2026-01-15T10:30:00\",\n \"nextResetAt\": \"2026-01-15T10:30:10\"\n }"
post:
tags:
- EUDR - Customers - Products
summary: Create a new outbound product
description: "\nCreate a new outbound product for EUDR compliance.\n\nOutbound products represent products that your organization sells or distributes.\nProvide at least `name` and `hsCode`; add `scientificName` and `reference` when available.\n\n**Required permission:** `access_public_products`\n "
operationId: addOutboundProduct
requestBody:
description: Outbound product creation payload.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicOutboundProductPayload'
examples:
Full product:
summary: Outbound product with scientific name and reference
description: Full product
value: '{"name":"Oak Dining Table","scientificName":"Quercus robur","reference":"OUT-2025-001","hsCode":"940360","annualRevenue":null,"annualQuantity":null,"unit":null}'
Basic product:
summary: Minimum required fields (name and HS code)
description: Basic product
value: '{"name":"Oak Dining Table","scientificName":null,"reference":null,"hsCode":"940360","annualRevenue":null,"annualQuantity":null,"unit":null}'
required: true
responses:
'200':
description: Outbound product created successfully. Returns the ID of the newly created product.
content:
application/json:
schema:
type: integer
description: ID of newly created product
examples:
Created product ID:
description: Created product ID
value: '301'
'400':
description: Errors if related data not found or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
HS Code not found:
description: HS Code not found
value:
code: hs_code_not_found
message: 'HS code not found: HSCode=123456'
solution: Check if the identifier provided is correct or create the record first
Supplier not found:
description: Supplier not found
value:
code: supplier_not_found
message: 'Supplier not found: ID=123456'
solution: Check if the identifier provided is correct or create the record first
Missing product scientific name:
description: Missing product scientific name
value:
code: missing_wooden_product_scientific_name
message: Missing product scientific name for wooden product
solution: Provide a scientific name for wooden products
'403':
description: '403 Forbidden - Authentication or authorization failure. This status code is returned when: (1) the request lacks valid authentication credentials (missing or invalid X-Auth-Token header), or (2) the authenticated user does not have the required permission to access this resource.'
content:
application/json:
schema:
$ref: '#/components/schemas/AccessDeniedErrorDTO'
examples:
Access denied example:
summary: User lacks necessary permissions or authentication
value: "{\n \"loggedIn\": true,\n \"code\": \"access_denied\",\n \"message\": \"Access denied: you don't have necessary permissions to access this resource\",\n \"solution\": \"Contact support for appropriate permissions\"\n }"
'500':
description: 500 Internal Server Error - An unexpected error occurred on the server. The request may or may not have been processed.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Error - Server Error:
summary: Unexpected server error
value: "{\n \"code\": \"internal_error\",\n \"message\": \"An unexpected error occurred\",\n \"solution\": \"Please try again later or contact support\"\n }"
'429':
description: '429 Too Many Requests - API rate limit exceeded. The request has been rejected because the rate limit for this endpoint has been exceeded. Default rate limits: GET requests - 100 per 10 seconds, 500 per minute; POST/PUT/PATCH/DELETE requests - 20 per 10 seconds, 100 per minute. For increased access, please contact customer success.'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiRateLimitResponse'
examples:
Rate limit exceeded example:
summary: API rate limit exceeded
value: "{\n \"error\": \"API rate limit exceeded\",\n \"message\": \"You have reached the maximum allowed requests. Please try again later or upgrade your plan for increased access\",\n \"requestLimit\": 20,\n \"requestCount\": 20,\n \"limits\": [\n {\n \"requestLimit\": 20,\n \"timeInSeconds\": 10\n },\n {\n \"requestLimit\": 100,\n \"timeInSeconds\": 60\n }\n ],\n \"currentTime\": \"2026-01-15T10:30:00\",\n \"nextResetAt\": \"2026-01-15T10:30:10\"\n }"
/public/v2/eudr/customers/products/outbound/{productId}/inbound:
post:
tags:
- EUDR - Customers - Products
summary: Connect inbound products to an outbound product
description: "\nLink one or more inbound products to an outbound product.\n\nThese relationships are used for EUDR compliance tracking and origin request management.\n\n**Required permission:** `access_public_products` and `UPDATE` on the outbound product\n "
operationId: addOutboundInboundProducts
parameters:
- name: productId
in: path
description: The ID of the outbound product.
required: true
schema:
type: integer
format: int32
example: 401
requestBody:
description: List of inbound product IDs to link to the outbound product.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicOutboundInboundProductPayload'
examples:
Link inbound products:
summary: Connect three inbound products to one outbound product
description: Link inbound products
value: '{"inboundProductIds":[201,202,203]}'
required: true
responses:
'200':
description: Inbound products linked successfully.
'400':
description: Invalid list (duplicates or type mismatch).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'404':
description: Outbound or inbound product not found.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'409':
description: Inbound product already linked.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'403':
description: '403 Forbidden - Authentication or authorization failure. This status code is returned when: (1) the request lacks valid authentication credentials (missing or invalid X-Auth-Token header), or (2) the authenticated user does not have the required permission to access this resource.'
content:
application/json:
schema:
$ref: '#/components/schemas/AccessDeniedErrorDTO'
examples:
Access denied example:
summary: User lacks necessary permissions or authentication
value: "{\n \"loggedIn\": true,\n \"code\": \"access_denied\",\n \"message\": \"Access denied: you don't have necessary permissions to access this resource\",\n \"solution\": \"Contact support for appropriate permissions\"\n }"
'500':
description: 500 Internal Server Error - An unexpected error occurred on the server. The request may or may not have been processed.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
examples:
Error - Server Error:
summary: Unexpected server error
va
# --- truncated at 32 KB (94 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/prewave/refs/heads/main/openapi/prewave-eudr-customers-products-api-openapi.yml